seleniumbase 4.33.10__py3-none-any.whl → 4.33.12__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 +0 -2
- seleniumbase/core/log_helper.py +15 -2
- seleniumbase/core/sb_cdp.py +112 -51
- seleniumbase/fixtures/base_case.py +23 -2
- seleniumbase/fixtures/page_actions.py +7 -2
- seleniumbase/undetected/cdp_driver/_contradict.py +3 -3
- seleniumbase/undetected/cdp_driver/browser.py +8 -6
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.dist-info}/METADATA +86 -76
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.dist-info}/RECORD +14 -14
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.dist-info}/LICENSE +0 -0
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.dist-info}/WHEEL +0 -0
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.33.10.dist-info → seleniumbase-4.33.12.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.12"
|
@@ -2407,8 +2407,6 @@ def _set_firefox_options(
|
|
2407
2407
|
options.set_preference("dom.webnotifications.enabled", False)
|
2408
2408
|
options.set_preference("dom.disable_beforeunload", True)
|
2409
2409
|
options.set_preference("browser.contentblocking.database.enabled", True)
|
2410
|
-
options.set_preference("extensions.allowPrivateBrowsingByDefault", True)
|
2411
|
-
options.set_preference("extensions.PrivateBrowsing.notification", False)
|
2412
2410
|
options.set_preference("extensions.systemAddon.update.enabled", False)
|
2413
2411
|
options.set_preference("extensions.update.autoUpdateDefault", False)
|
2414
2412
|
options.set_preference("extensions.update.enabled", False)
|
seleniumbase/core/log_helper.py
CHANGED
@@ -15,6 +15,11 @@ if sys.version_info >= (3, 11):
|
|
15
15
|
py311_patch2 = constants.PatchPy311.PATCH
|
16
16
|
|
17
17
|
|
18
|
+
def __is_cdp_swap_needed(driver):
|
19
|
+
"""If the driver is disconnected, use a CDP method when available."""
|
20
|
+
return shared_utils.is_cdp_swap_needed(driver)
|
21
|
+
|
22
|
+
|
18
23
|
def log_screenshot(test_logpath, driver, screenshot=None, get=False):
|
19
24
|
screenshot_name = settings.SCREENSHOT_NAME
|
20
25
|
screenshot_path = os.path.join(test_logpath, screenshot_name)
|
@@ -356,7 +361,11 @@ def log_page_source(test_logpath, driver, source=None):
|
|
356
361
|
page_source = source
|
357
362
|
else:
|
358
363
|
try:
|
359
|
-
page_source =
|
364
|
+
page_source = None
|
365
|
+
if __is_cdp_swap_needed(driver):
|
366
|
+
page_source = driver.cdp.get_page_source()
|
367
|
+
else:
|
368
|
+
page_source = driver.page_source
|
360
369
|
page_source = get_html_source_with_base_href(driver, page_source)
|
361
370
|
except Exception:
|
362
371
|
source = constants.Warnings.PAGE_SOURCE_UNDEFINED
|
@@ -448,7 +457,11 @@ def get_test_name(test):
|
|
448
457
|
|
449
458
|
def get_last_page(driver):
|
450
459
|
try:
|
451
|
-
last_page =
|
460
|
+
last_page = None
|
461
|
+
if __is_cdp_swap_needed(driver):
|
462
|
+
last_page = driver.cdp.get_current_url()
|
463
|
+
else:
|
464
|
+
last_page = driver.current_url
|
452
465
|
except Exception:
|
453
466
|
last_page = "[WARNING! Browser Not Open!]"
|
454
467
|
if len(last_page) < 5:
|
seleniumbase/core/sb_cdp.py
CHANGED
@@ -127,14 +127,14 @@ class CDPMethods():
|
|
127
127
|
def add_handler(self, event, handler):
|
128
128
|
self.page.add_handler(event, handler)
|
129
129
|
|
130
|
-
def find_element(
|
131
|
-
self, selector, best_match=False, timeout=settings.SMALL_TIMEOUT
|
132
|
-
):
|
130
|
+
def find_element(self, selector, best_match=False, timeout=None):
|
133
131
|
"""Similar to select(), but also finds elements by text content.
|
134
132
|
When using text-based searches, if best_match=False, then will
|
135
133
|
find the first element with the text. If best_match=True, then
|
136
134
|
if multiple elements have that text, then will use the element
|
137
135
|
with the closest text-length to the text being searched for."""
|
136
|
+
if not timeout:
|
137
|
+
timeout = settings.SMALL_TIMEOUT
|
138
138
|
self.__add_light_pause()
|
139
139
|
selector = self.__convert_to_css_if_xpath(selector)
|
140
140
|
early_failure = False
|
@@ -167,12 +167,12 @@ class CDPMethods():
|
|
167
167
|
self.__slow_mode_pause_if_set()
|
168
168
|
return element
|
169
169
|
|
170
|
-
def find_element_by_text(
|
171
|
-
self, text, tag_name=None, timeout=settings.SMALL_TIMEOUT
|
172
|
-
):
|
170
|
+
def find_element_by_text(self, text, tag_name=None, timeout=None):
|
173
171
|
"""Returns an element by matching text.
|
174
172
|
Optionally, provide a tag_name to narrow down the search to an
|
175
173
|
element with the given tag. (Eg: a, button, div, script, span)"""
|
174
|
+
if not timeout:
|
175
|
+
timeout = settings.SMALL_TIMEOUT
|
176
176
|
self.__add_light_pause()
|
177
177
|
time_now = time.time()
|
178
178
|
self.assert_text(text, timeout=timeout)
|
@@ -218,7 +218,9 @@ class CDPMethods():
|
|
218
218
|
% (text, tag_name, timeout, plural)
|
219
219
|
)
|
220
220
|
|
221
|
-
def find_all(self, selector, timeout=
|
221
|
+
def find_all(self, selector, timeout=None):
|
222
|
+
if not timeout:
|
223
|
+
timeout = settings.SMALL_TIMEOUT
|
222
224
|
self.__add_light_pause()
|
223
225
|
selector = self.__convert_to_css_if_xpath(selector)
|
224
226
|
elements = self.loop.run_until_complete(
|
@@ -272,8 +274,10 @@ class CDPMethods():
|
|
272
274
|
updated_elements.append(element)
|
273
275
|
return updated_elements
|
274
276
|
|
275
|
-
def select(self, selector, timeout=
|
277
|
+
def select(self, selector, timeout=None):
|
276
278
|
"""Similar to find_element(), but without text-based search."""
|
279
|
+
if not timeout:
|
280
|
+
timeout = settings.SMALL_TIMEOUT
|
277
281
|
self.__add_light_pause()
|
278
282
|
selector = self.__convert_to_css_if_xpath(selector)
|
279
283
|
if (":contains(" in selector):
|
@@ -307,7 +311,9 @@ class CDPMethods():
|
|
307
311
|
self.__slow_mode_pause_if_set()
|
308
312
|
return element
|
309
313
|
|
310
|
-
def select_all(self, selector, timeout=
|
314
|
+
def select_all(self, selector, timeout=None):
|
315
|
+
if not timeout:
|
316
|
+
timeout = settings.SMALL_TIMEOUT
|
311
317
|
self.__add_light_pause()
|
312
318
|
selector = self.__convert_to_css_if_xpath(selector)
|
313
319
|
elements = self.loop.run_until_complete(
|
@@ -319,10 +325,14 @@ class CDPMethods():
|
|
319
325
|
updated_elements.append(element)
|
320
326
|
return updated_elements
|
321
327
|
|
322
|
-
def find_elements(self, selector, timeout=
|
328
|
+
def find_elements(self, selector, timeout=None):
|
329
|
+
if not timeout:
|
330
|
+
timeout = settings.SMALL_TIMEOUT
|
323
331
|
return self.select_all(selector, timeout=timeout)
|
324
332
|
|
325
|
-
def find_visible_elements(self, selector, timeout=
|
333
|
+
def find_visible_elements(self, selector, timeout=None):
|
334
|
+
if not timeout:
|
335
|
+
timeout = settings.SMALL_TIMEOUT
|
326
336
|
visible_elements = []
|
327
337
|
elements = self.select_all(selector, timeout=timeout)
|
328
338
|
for element in elements:
|
@@ -519,7 +529,20 @@ class CDPMethods():
|
|
519
529
|
try:
|
520
530
|
return element.get_js_attributes()[attribute]
|
521
531
|
except Exception:
|
522
|
-
|
532
|
+
if not attribute:
|
533
|
+
raise
|
534
|
+
try:
|
535
|
+
attribute_str = element.get_js_attributes()
|
536
|
+
locate = ' %s="' % attribute
|
537
|
+
if locate in attribute_str.outerHTML:
|
538
|
+
outer_html = attribute_str.outerHTML
|
539
|
+
attr_start = outer_html.find(locate) + len(locate)
|
540
|
+
attr_end = outer_html.find('"', attr_start)
|
541
|
+
value = outer_html[attr_start:attr_end]
|
542
|
+
return value
|
543
|
+
except Exception:
|
544
|
+
pass
|
545
|
+
return None
|
523
546
|
|
524
547
|
def __get_x_scroll_offset(self):
|
525
548
|
x_scroll_offset = self.loop.run_until_complete(
|
@@ -574,12 +597,12 @@ class CDPMethods():
|
|
574
597
|
driver.cookies.load(*args, **kwargs)
|
575
598
|
)
|
576
599
|
|
577
|
-
def clear_cookies(self
|
600
|
+
def clear_cookies(self):
|
578
601
|
driver = self.driver
|
579
602
|
if hasattr(driver, "cdp_base"):
|
580
603
|
driver = driver.cdp_base
|
581
604
|
return self.loop.run_until_complete(
|
582
|
-
driver.cookies.clear(
|
605
|
+
driver.cookies.clear()
|
583
606
|
)
|
584
607
|
|
585
608
|
def sleep(self, seconds):
|
@@ -603,7 +626,9 @@ class CDPMethods():
|
|
603
626
|
self.page.evaluate(js_code)
|
604
627
|
)
|
605
628
|
|
606
|
-
def click(self, selector, timeout=
|
629
|
+
def click(self, selector, timeout=None):
|
630
|
+
if not timeout:
|
631
|
+
timeout = settings.SMALL_TIMEOUT
|
607
632
|
self.__slow_mode_pause_if_set()
|
608
633
|
element = self.find_element(selector, timeout=timeout)
|
609
634
|
element.scroll_into_view()
|
@@ -620,11 +645,12 @@ class CDPMethods():
|
|
620
645
|
|
621
646
|
def click_if_visible(self, selector):
|
622
647
|
if self.is_element_visible(selector):
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
648
|
+
with suppress(Exception):
|
649
|
+
element = self.find_element(selector, timeout=0)
|
650
|
+
element.scroll_into_view()
|
651
|
+
element.click()
|
652
|
+
self.__slow_mode_pause_if_set()
|
653
|
+
self.loop.run_until_complete(self.page.wait())
|
628
654
|
|
629
655
|
def click_visible_elements(self, selector, limit=0):
|
630
656
|
"""Finds all matching page elements and clicks visible ones in order.
|
@@ -658,8 +684,10 @@ class CDPMethods():
|
|
658
684
|
except Exception:
|
659
685
|
break
|
660
686
|
|
661
|
-
def mouse_click(self, selector, timeout=
|
687
|
+
def mouse_click(self, selector, timeout=None):
|
662
688
|
"""(Attempt simulating a mouse click)"""
|
689
|
+
if not timeout:
|
690
|
+
timeout = settings.SMALL_TIMEOUT
|
663
691
|
self.__slow_mode_pause_if_set()
|
664
692
|
element = self.find_element(selector, timeout=timeout)
|
665
693
|
element.scroll_into_view()
|
@@ -757,7 +785,9 @@ class CDPMethods():
|
|
757
785
|
with suppress(Exception):
|
758
786
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
759
787
|
|
760
|
-
def send_keys(self, selector, text, timeout=
|
788
|
+
def send_keys(self, selector, text, timeout=None):
|
789
|
+
if not timeout:
|
790
|
+
timeout = settings.SMALL_TIMEOUT
|
761
791
|
self.__slow_mode_pause_if_set()
|
762
792
|
element = self.select(selector, timeout=timeout)
|
763
793
|
element.scroll_into_view()
|
@@ -767,8 +797,10 @@ class CDPMethods():
|
|
767
797
|
self.__slow_mode_pause_if_set()
|
768
798
|
self.loop.run_until_complete(self.page.wait())
|
769
799
|
|
770
|
-
def press_keys(self, selector, text, timeout=
|
800
|
+
def press_keys(self, selector, text, timeout=None):
|
771
801
|
"""Similar to send_keys(), but presses keys at human speed."""
|
802
|
+
if not timeout:
|
803
|
+
timeout = settings.SMALL_TIMEOUT
|
772
804
|
self.__slow_mode_pause_if_set()
|
773
805
|
element = self.select(selector, timeout=timeout)
|
774
806
|
element.scroll_into_view()
|
@@ -785,8 +817,10 @@ class CDPMethods():
|
|
785
817
|
self.__slow_mode_pause_if_set()
|
786
818
|
self.loop.run_until_complete(self.page.wait())
|
787
819
|
|
788
|
-
def type(self, selector, text, timeout=
|
820
|
+
def type(self, selector, text, timeout=None):
|
789
821
|
"""Similar to send_keys(), but clears the text field first."""
|
822
|
+
if not timeout:
|
823
|
+
timeout = settings.SMALL_TIMEOUT
|
790
824
|
self.__slow_mode_pause_if_set()
|
791
825
|
element = self.select(selector, timeout=timeout)
|
792
826
|
element.scroll_into_view()
|
@@ -798,8 +832,10 @@ class CDPMethods():
|
|
798
832
|
self.__slow_mode_pause_if_set()
|
799
833
|
self.loop.run_until_complete(self.page.wait())
|
800
834
|
|
801
|
-
def set_value(self, selector, text, timeout=
|
835
|
+
def set_value(self, selector, text, timeout=None):
|
802
836
|
"""Similar to send_keys(), but clears the text field first."""
|
837
|
+
if not timeout:
|
838
|
+
timeout = settings.SMALL_TIMEOUT
|
803
839
|
self.__slow_mode_pause_if_set()
|
804
840
|
selector = self.__convert_to_css_if_xpath(selector)
|
805
841
|
element = self.select(selector, timeout=timeout)
|
@@ -1022,7 +1058,9 @@ class CDPMethods():
|
|
1022
1058
|
coordinates["y"] = y if y else 0
|
1023
1059
|
return coordinates
|
1024
1060
|
|
1025
|
-
def get_element_rect(self, selector, timeout=
|
1061
|
+
def get_element_rect(self, selector, timeout=None):
|
1062
|
+
if not timeout:
|
1063
|
+
timeout = settings.SMALL_TIMEOUT
|
1026
1064
|
selector = self.__convert_to_css_if_xpath(selector)
|
1027
1065
|
self.select(selector, timeout=timeout)
|
1028
1066
|
self.__add_light_pause()
|
@@ -1035,23 +1073,29 @@ class CDPMethods():
|
|
1035
1073
|
)
|
1036
1074
|
return coordinates
|
1037
1075
|
|
1038
|
-
def get_element_size(self, selector):
|
1039
|
-
|
1076
|
+
def get_element_size(self, selector, timeout=None):
|
1077
|
+
if not timeout:
|
1078
|
+
timeout = settings.SMALL_TIMEOUT
|
1079
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1040
1080
|
coordinates = {}
|
1041
1081
|
coordinates["width"] = element_rect["width"]
|
1042
1082
|
coordinates["height"] = element_rect["height"]
|
1043
1083
|
return coordinates
|
1044
1084
|
|
1045
|
-
def get_element_position(self, selector):
|
1046
|
-
|
1085
|
+
def get_element_position(self, selector, timeout=None):
|
1086
|
+
if not timeout:
|
1087
|
+
timeout = settings.SMALL_TIMEOUT
|
1088
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1047
1089
|
coordinates = {}
|
1048
1090
|
coordinates["x"] = element_rect["x"]
|
1049
1091
|
coordinates["y"] = element_rect["y"]
|
1050
1092
|
return coordinates
|
1051
1093
|
|
1052
|
-
def get_gui_element_rect(self, selector):
|
1094
|
+
def get_gui_element_rect(self, selector, timeout=None):
|
1053
1095
|
"""(Coordinates are relative to the screen. Not the window.)"""
|
1054
|
-
|
1096
|
+
if not timeout:
|
1097
|
+
timeout = settings.SMALL_TIMEOUT
|
1098
|
+
element_rect = self.get_element_rect(selector, timeout=timeout)
|
1055
1099
|
e_width = element_rect["width"]
|
1056
1100
|
e_height = element_rect["height"]
|
1057
1101
|
window_rect = self.get_window_rect()
|
@@ -1065,9 +1109,11 @@ class CDPMethods():
|
|
1065
1109
|
y = y + window_rect["scrollY"]
|
1066
1110
|
return ({"height": e_height, "width": e_width, "x": x, "y": y})
|
1067
1111
|
|
1068
|
-
def get_gui_element_center(self, selector):
|
1112
|
+
def get_gui_element_center(self, selector, timeout=None):
|
1069
1113
|
"""(Coordinates are relative to the screen. Not the window.)"""
|
1070
|
-
|
1114
|
+
if not timeout:
|
1115
|
+
timeout = settings.SMALL_TIMEOUT
|
1116
|
+
element_rect = self.get_gui_element_rect(selector, timeout=timeout)
|
1071
1117
|
e_width = element_rect["width"]
|
1072
1118
|
e_height = element_rect["height"]
|
1073
1119
|
e_x = element_rect["x"]
|
@@ -1094,7 +1140,14 @@ class CDPMethods():
|
|
1094
1140
|
)
|
1095
1141
|
|
1096
1142
|
def get_element_attribute(self, selector, attribute):
|
1097
|
-
|
1143
|
+
attributes = self.get_element_attributes(selector)
|
1144
|
+
with suppress(Exception):
|
1145
|
+
return attributes[attribute]
|
1146
|
+
locate = ' %s="' % attribute
|
1147
|
+
value = self.get_attribute(selector, attribute)
|
1148
|
+
if not value and locate not in attributes:
|
1149
|
+
raise KeyError(attribute)
|
1150
|
+
return value
|
1098
1151
|
|
1099
1152
|
def get_attribute(self, selector, attribute):
|
1100
1153
|
return self.find_element(selector).get_attribute(attribute)
|
@@ -1608,9 +1661,9 @@ class CDPMethods():
|
|
1608
1661
|
return True
|
1609
1662
|
return False
|
1610
1663
|
|
1611
|
-
def wait_for_element_visible(
|
1612
|
-
|
1613
|
-
|
1664
|
+
def wait_for_element_visible(self, selector, timeout=None):
|
1665
|
+
if not timeout:
|
1666
|
+
timeout = settings.SMALL_TIMEOUT
|
1614
1667
|
try:
|
1615
1668
|
self.select(selector, timeout=timeout)
|
1616
1669
|
except Exception:
|
@@ -1621,8 +1674,10 @@ class CDPMethods():
|
|
1621
1674
|
time.sleep(0.1)
|
1622
1675
|
raise Exception("Element {%s} was not visible!" % selector)
|
1623
1676
|
|
1624
|
-
def assert_element(self, selector, timeout=
|
1677
|
+
def assert_element(self, selector, timeout=None):
|
1625
1678
|
"""Same as assert_element_visible()"""
|
1679
|
+
if not timeout:
|
1680
|
+
timeout = settings.SMALL_TIMEOUT
|
1626
1681
|
try:
|
1627
1682
|
self.select(selector, timeout=timeout)
|
1628
1683
|
except Exception:
|
@@ -1633,8 +1688,10 @@ class CDPMethods():
|
|
1633
1688
|
time.sleep(0.1)
|
1634
1689
|
raise Exception("Element {%s} was not visible!" % selector)
|
1635
1690
|
|
1636
|
-
def assert_element_visible(self, selector, timeout=
|
1691
|
+
def assert_element_visible(self, selector, timeout=None):
|
1637
1692
|
"""Same as assert_element()"""
|
1693
|
+
if not timeout:
|
1694
|
+
timeout = settings.SMALL_TIMEOUT
|
1638
1695
|
try:
|
1639
1696
|
self.select(selector, timeout=timeout)
|
1640
1697
|
except Exception:
|
@@ -1645,16 +1702,20 @@ class CDPMethods():
|
|
1645
1702
|
time.sleep(0.1)
|
1646
1703
|
raise Exception("Element {%s} was not visible!" % selector)
|
1647
1704
|
|
1648
|
-
def assert_element_present(self, selector, timeout=
|
1705
|
+
def assert_element_present(self, selector, timeout=None):
|
1649
1706
|
"""Assert element is present in the DOM. (Visibility NOT required)"""
|
1707
|
+
if not timeout:
|
1708
|
+
timeout = settings.SMALL_TIMEOUT
|
1650
1709
|
try:
|
1651
1710
|
self.select(selector, timeout=timeout)
|
1652
1711
|
except Exception:
|
1653
1712
|
raise Exception("Element {%s} was not found!" % selector)
|
1654
1713
|
return True
|
1655
1714
|
|
1656
|
-
def assert_element_absent(self, selector, timeout=
|
1715
|
+
def assert_element_absent(self, selector, timeout=None):
|
1657
1716
|
"""Assert element is not present in the DOM."""
|
1717
|
+
if not timeout:
|
1718
|
+
timeout = settings.SMALL_TIMEOUT
|
1658
1719
|
start_ms = time.time() * 1000.0
|
1659
1720
|
stop_ms = start_ms + (timeout * 1000.0)
|
1660
1721
|
for i in range(int(timeout * 10)):
|
@@ -1672,10 +1733,10 @@ class CDPMethods():
|
|
1672
1733
|
% (selector, timeout, plural)
|
1673
1734
|
)
|
1674
1735
|
|
1675
|
-
def assert_element_not_visible(
|
1676
|
-
self, selector, timeout=settings.SMALL_TIMEOUT
|
1677
|
-
):
|
1736
|
+
def assert_element_not_visible(self, selector, timeout=None):
|
1678
1737
|
"""Assert element is not visible on page. (May still be in DOM)"""
|
1738
|
+
if not timeout:
|
1739
|
+
timeout = settings.SMALL_TIMEOUT
|
1679
1740
|
start_ms = time.time() * 1000.0
|
1680
1741
|
stop_ms = start_ms + (timeout * 1000.0)
|
1681
1742
|
for i in range(int(timeout * 10)):
|
@@ -1770,9 +1831,9 @@ class CDPMethods():
|
|
1770
1831
|
if expected not in actual:
|
1771
1832
|
raise Exception(error % (expected, actual))
|
1772
1833
|
|
1773
|
-
def assert_text(
|
1774
|
-
|
1775
|
-
|
1834
|
+
def assert_text(self, text, selector="body", timeout=None):
|
1835
|
+
if not timeout:
|
1836
|
+
timeout = settings.SMALL_TIMEOUT
|
1776
1837
|
start_ms = time.time() * 1000.0
|
1777
1838
|
stop_ms = start_ms + (timeout * 1000.0)
|
1778
1839
|
text = text.strip()
|
@@ -1795,9 +1856,9 @@ class CDPMethods():
|
|
1795
1856
|
% (text, selector, element.text_all)
|
1796
1857
|
)
|
1797
1858
|
|
1798
|
-
def assert_exact_text(
|
1799
|
-
|
1800
|
-
|
1859
|
+
def assert_exact_text(self, text, selector="body", timeout=None):
|
1860
|
+
if not timeout:
|
1861
|
+
timeout = settings.SMALL_TIMEOUT
|
1801
1862
|
start_ms = time.time() * 1000.0
|
1802
1863
|
stop_ms = start_ms + (timeout * 1000.0)
|
1803
1864
|
text = text.strip()
|
@@ -3416,6 +3416,14 @@ class BaseCase(unittest.TestCase):
|
|
3416
3416
|
self.activate_jquery()
|
3417
3417
|
return self.driver.execute_script(script, *args, **kwargs)
|
3418
3418
|
|
3419
|
+
def get_element_at_x_y(self, x, y):
|
3420
|
+
"""Return element at current window's x,y coordinates."""
|
3421
|
+
self.__check_scope()
|
3422
|
+
self._check_browser()
|
3423
|
+
return self.execute_script(
|
3424
|
+
"return document.elementFromPoint(%s, %s);" % (x, y)
|
3425
|
+
)
|
3426
|
+
|
3419
3427
|
def get_gui_element_rect(self, selector, by="css selector"):
|
3420
3428
|
"""Very similar to element.rect, but the x, y coordinates are
|
3421
3429
|
relative to the entire screen, rather than the browser window.
|
@@ -4484,7 +4492,8 @@ class BaseCase(unittest.TestCase):
|
|
4484
4492
|
@Params
|
4485
4493
|
name - The file name to save the current page's HTML to.
|
4486
4494
|
folder - The folder to save the file to. (Default = current folder)"""
|
4487
|
-
self.
|
4495
|
+
if not self.__is_cdp_swap_needed():
|
4496
|
+
self.wait_for_ready_state_complete()
|
4488
4497
|
return page_actions.save_page_source(self.driver, name, folder)
|
4489
4498
|
|
4490
4499
|
def save_cookies(self, name="cookies.txt"):
|
@@ -4542,6 +4551,9 @@ class BaseCase(unittest.TestCase):
|
|
4542
4551
|
def delete_all_cookies(self):
|
4543
4552
|
"""Deletes all cookies in the web browser.
|
4544
4553
|
Does NOT delete the saved cookies file."""
|
4554
|
+
if self.__is_cdp_swap_needed():
|
4555
|
+
self.cdp.clear_cookies()
|
4556
|
+
return
|
4545
4557
|
self.wait_for_ready_state_complete()
|
4546
4558
|
self.driver.delete_all_cookies()
|
4547
4559
|
if self.recorder_mode:
|
@@ -4553,7 +4565,6 @@ class BaseCase(unittest.TestCase):
|
|
4553
4565
|
def delete_saved_cookies(self, name="cookies.txt"):
|
4554
4566
|
"""Deletes the cookies file from the "saved_cookies" folder.
|
4555
4567
|
Does NOT delete the cookies from the web browser."""
|
4556
|
-
self.wait_for_ready_state_complete()
|
4557
4568
|
if name.endswith("/"):
|
4558
4569
|
raise Exception("Invalid filename for Cookies!")
|
4559
4570
|
if "/" in name:
|
@@ -4592,14 +4603,20 @@ class BaseCase(unittest.TestCase):
|
|
4592
4603
|
return json.loads(json_cookies)
|
4593
4604
|
|
4594
4605
|
def get_cookie(self, name):
|
4606
|
+
self.__check_scope()
|
4607
|
+
self._check_browser()
|
4595
4608
|
return self.driver.get_cookie(name)
|
4596
4609
|
|
4597
4610
|
def get_cookies(self):
|
4611
|
+
self.__check_scope()
|
4612
|
+
self._check_browser()
|
4598
4613
|
return self.driver.get_cookies()
|
4599
4614
|
|
4600
4615
|
def get_cookie_string(self):
|
4616
|
+
self.__check_scope()
|
4601
4617
|
if self.__is_cdp_swap_needed():
|
4602
4618
|
return self.cdp.get_cookie_string()
|
4619
|
+
self._check_browser()
|
4603
4620
|
return self.execute_script("return document.cookie;")
|
4604
4621
|
|
4605
4622
|
def add_cookie(self, cookie_dict, expiry=False):
|
@@ -4614,6 +4631,8 @@ class BaseCase(unittest.TestCase):
|
|
4614
4631
|
If expiry > 0: Set "expiry" to expiry minutes in the future.
|
4615
4632
|
If expiry == True: Set "expiry" to 24 hours in the future.
|
4616
4633
|
"""
|
4634
|
+
self.__check_scope()
|
4635
|
+
self._check_browser()
|
4617
4636
|
cookie = cookie_dict
|
4618
4637
|
if "domain" in cookie:
|
4619
4638
|
origin = self.get_origin()
|
@@ -4638,6 +4657,8 @@ class BaseCase(unittest.TestCase):
|
|
4638
4657
|
If expiry > 0: Set "expiry" to expiry minutes in the future.
|
4639
4658
|
If expiry == True: Set "expiry" to 24 hours in the future.
|
4640
4659
|
"""
|
4660
|
+
self.__check_scope()
|
4661
|
+
self._check_browser()
|
4641
4662
|
origin = self.get_origin()
|
4642
4663
|
trim_origin = origin.split("://")[-1]
|
4643
4664
|
for cookie in cookies:
|
@@ -1342,7 +1342,8 @@ def save_page_source(driver, name, folder=None):
|
|
1342
1342
|
"""
|
1343
1343
|
from seleniumbase.core import log_helper
|
1344
1344
|
|
1345
|
-
|
1345
|
+
if not __is_cdp_swap_needed(driver):
|
1346
|
+
_reconnect_if_disconnected(driver) # If disconnected without CDP
|
1346
1347
|
if not name.endswith(".html"):
|
1347
1348
|
name = name + ".html"
|
1348
1349
|
if folder:
|
@@ -1353,7 +1354,11 @@ def save_page_source(driver, name, folder=None):
|
|
1353
1354
|
html_file_path = os.path.join(file_path, name)
|
1354
1355
|
else:
|
1355
1356
|
html_file_path = name
|
1356
|
-
page_source =
|
1357
|
+
page_source = None
|
1358
|
+
if __is_cdp_swap_needed(driver):
|
1359
|
+
page_source = driver.cdp.get_page_source()
|
1360
|
+
else:
|
1361
|
+
page_source = driver.page_source
|
1357
1362
|
html_file = codecs.open(html_file_path, "w+", "utf-8")
|
1358
1363
|
rendered_source = log_helper.get_html_source_with_base_href(
|
1359
1364
|
driver, page_source
|
@@ -31,12 +31,12 @@ class ContraDict(dict):
|
|
31
31
|
|
32
32
|
def __init__(self, *args, **kwargs):
|
33
33
|
super().__init__()
|
34
|
-
silent = kwargs.pop("silent", False)
|
34
|
+
# silent = kwargs.pop("silent", False)
|
35
35
|
_ = dict(*args, **kwargs)
|
36
36
|
|
37
37
|
super().__setattr__("__dict__", self)
|
38
38
|
for k, v in _.items():
|
39
|
-
_check_key(k, self, False,
|
39
|
+
_check_key(k, self, False, True)
|
40
40
|
super().__setitem__(k, _wrap(self.__class__, v))
|
41
41
|
|
42
42
|
def __setitem__(self, key, value):
|
@@ -90,7 +90,7 @@ _warning_names_message = """\n\
|
|
90
90
|
|
91
91
|
|
92
92
|
def _check_key(
|
93
|
-
key: str, mapping: _Mapping, boolean: bool = False, silent=
|
93
|
+
key: str, mapping: _Mapping, boolean: bool = False, silent=True
|
94
94
|
):
|
95
95
|
"""Checks `key` and warns if needed.
|
96
96
|
:param key:
|
@@ -10,6 +10,7 @@ import pathlib
|
|
10
10
|
import pickle
|
11
11
|
import re
|
12
12
|
import shutil
|
13
|
+
import time
|
13
14
|
import urllib.parse
|
14
15
|
import urllib.request
|
15
16
|
import warnings
|
@@ -30,8 +31,6 @@ def get_registered_instances():
|
|
30
31
|
|
31
32
|
|
32
33
|
def deconstruct_browser():
|
33
|
-
import time
|
34
|
-
|
35
34
|
for _ in __registered__instances__:
|
36
35
|
if not _.stopped:
|
37
36
|
_.stop()
|
@@ -117,8 +116,13 @@ class Browser:
|
|
117
116
|
port=port,
|
118
117
|
**kwargs,
|
119
118
|
)
|
120
|
-
|
121
|
-
|
119
|
+
try:
|
120
|
+
instance = cls(config)
|
121
|
+
await instance.start()
|
122
|
+
except Exception:
|
123
|
+
time.sleep(0.15)
|
124
|
+
instance = cls(config)
|
125
|
+
await instance.start()
|
122
126
|
return instance
|
123
127
|
|
124
128
|
def __init__(self, config: Config, **kwargs):
|
@@ -379,8 +383,6 @@ class Browser:
|
|
379
383
|
--------------------------------
|
380
384
|
Failed to connect to the browser
|
381
385
|
--------------------------------
|
382
|
-
Possibly because you are running as "root".
|
383
|
-
If so, you may need to use no_sandbox=True.
|
384
386
|
"""
|
385
387
|
)
|
386
388
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: seleniumbase
|
3
|
-
Version: 4.33.
|
3
|
+
Version: 4.33.12
|
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
|
@@ -64,8 +64,8 @@ Requires-Dist: packaging>=24.2
|
|
64
64
|
Requires-Dist: setuptools~=70.2; python_version < "3.10"
|
65
65
|
Requires-Dist: setuptools>=75.6.0; python_version >= "3.10"
|
66
66
|
Requires-Dist: wheel>=0.45.1
|
67
|
-
Requires-Dist: attrs>=24.
|
68
|
-
Requires-Dist: certifi>=2024.
|
67
|
+
Requires-Dist: attrs>=24.3.0
|
68
|
+
Requires-Dist: certifi>=2024.12.14
|
69
69
|
Requires-Dist: exceptiongroup>=1.2.2
|
70
70
|
Requires-Dist: websockets~=13.1; python_version < "3.9"
|
71
71
|
Requires-Dist: websockets>=14.1; python_version >= "3.9"
|
@@ -75,7 +75,10 @@ Requires-Dist: mycdp>=1.1.0
|
|
75
75
|
Requires-Dist: pynose>=1.5.3
|
76
76
|
Requires-Dist: platformdirs>=4.3.6
|
77
77
|
Requires-Dist: typing-extensions>=4.12.2
|
78
|
-
Requires-Dist: sbvirtualdisplay>=1.
|
78
|
+
Requires-Dist: sbvirtualdisplay>=1.4.0
|
79
|
+
Requires-Dist: MarkupSafe==2.1.5; python_version < "3.9"
|
80
|
+
Requires-Dist: MarkupSafe>=3.0.2; python_version >= "3.9"
|
81
|
+
Requires-Dist: Jinja2>=3.1.5
|
79
82
|
Requires-Dist: six>=1.17.0
|
80
83
|
Requires-Dist: parse>=1.20.2
|
81
84
|
Requires-Dist: parse-type>=0.6.4
|
@@ -87,9 +90,9 @@ Requires-Dist: tabcompleter>=1.4.0
|
|
87
90
|
Requires-Dist: pdbp>=1.6.1
|
88
91
|
Requires-Dist: idna==3.10
|
89
92
|
Requires-Dist: chardet==5.2.0
|
90
|
-
Requires-Dist: charset-normalizer==3.4.
|
93
|
+
Requires-Dist: charset-normalizer==3.4.1
|
91
94
|
Requires-Dist: urllib3<2,>=1.26.20; python_version < "3.10"
|
92
|
-
Requires-Dist: urllib3<2.
|
95
|
+
Requires-Dist: urllib3<2.4.0,>=1.26.20; python_version >= "3.10"
|
93
96
|
Requires-Dist: requests==2.32.3
|
94
97
|
Requires-Dist: sniffio==1.3.1
|
95
98
|
Requires-Dist: h11==0.14.0
|
@@ -236,21 +239,76 @@ Requires-Dist: zstandard==0.23.0; extra == "selenium-wire"
|
|
236
239
|
|
237
240
|
📚 Learn from [**over 200 examples** in the **SeleniumBase/examples/** folder](https://github.com/seleniumbase/SeleniumBase/tree/master/examples).
|
238
241
|
|
239
|
-
|
242
|
+
🐙 Note that <a translate="no" href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md"><b>UC Mode</b></a> / <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md"><b>CDP Mode</b></a> (Stealth Mode) have their own ReadMe files.
|
240
243
|
|
241
|
-
|
244
|
+
ℹ️ Most scripts run with raw <code translate="no"><b>python</b></code>, although some scripts use <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md">Syntax Formats</a> that expect <a href="https://docs.pytest.org/en/latest/how-to/usage.html" translate="no"><b>pytest</b></a> (a Python unit-testing framework included with SeleniumBase that can discover, collect, and run tests automatically).
|
242
245
|
|
243
|
-
|
246
|
+
--------
|
244
247
|
|
245
|
-
<p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/
|
248
|
+
<p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py">raw_google.py</a>, which performs a Google search:</p>
|
246
249
|
|
247
|
-
```
|
248
|
-
|
250
|
+
```python
|
251
|
+
from seleniumbase import SB
|
252
|
+
|
253
|
+
with SB(test=True) as sb:
|
254
|
+
sb.open("https://google.com/ncr")
|
255
|
+
sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
|
256
|
+
sb.click('[href*="github.com/seleniumbase/"]')
|
257
|
+
sb.save_screenshot_to_logs() # ./latest_logs/
|
258
|
+
print(sb.get_page_title())
|
249
259
|
```
|
250
260
|
|
251
|
-
|
261
|
+
> `python raw_google.py`
|
262
|
+
|
263
|
+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_google.py"><img src="https://seleniumbase.github.io/cdn/gif/google_search.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="420" /></a>
|
264
|
+
|
265
|
+
--------
|
266
|
+
|
267
|
+
<p align="left">📗 Here's an example of bypassing Cloudflare's challenge page: <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/raw_gitlab.py">SeleniumBase/examples/cdp_mode/raw_gitlab.py</a></p>
|
252
268
|
|
253
|
-
|
269
|
+
```python
|
270
|
+
from seleniumbase import SB
|
271
|
+
|
272
|
+
with SB(uc=True, test=True, locale_code="en") as sb:
|
273
|
+
url = "https://gitlab.com/users/sign_in"
|
274
|
+
sb.activate_cdp_mode(url)
|
275
|
+
sb.uc_gui_click_captcha()
|
276
|
+
sb.sleep(2)
|
277
|
+
```
|
278
|
+
|
279
|
+
<img src="https://seleniumbase.github.io/other/cf_sec.jpg" title="SeleniumBase" width="332"> <img src="https://seleniumbase.github.io/other/gitlab_bypass.png" title="SeleniumBase" width="288">
|
280
|
+
|
281
|
+
--------
|
282
|
+
|
283
|
+
<p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">test_get_swag.py</a>, which tests an e-commerce site:</p>
|
284
|
+
|
285
|
+
```python
|
286
|
+
from seleniumbase import BaseCase
|
287
|
+
BaseCase.main(__name__, __file__) # Call pytest
|
288
|
+
|
289
|
+
class MyTestClass(BaseCase):
|
290
|
+
def test_swag_labs(self):
|
291
|
+
self.open("https://www.saucedemo.com")
|
292
|
+
self.type("#user-name", "standard_user")
|
293
|
+
self.type("#password", "secret_sauce\n")
|
294
|
+
self.assert_element("div.inventory_list")
|
295
|
+
self.click('button[name*="backpack"]')
|
296
|
+
self.click("#shopping_cart_container a")
|
297
|
+
self.assert_text("Backpack", "div.cart_item")
|
298
|
+
self.click("button#checkout")
|
299
|
+
self.type("input#first-name", "SeleniumBase")
|
300
|
+
self.type("input#last-name", "Automation")
|
301
|
+
self.type("input#postal-code", "77123")
|
302
|
+
self.click("input#continue")
|
303
|
+
self.click("button#finish")
|
304
|
+
self.assert_text("Thank you for your order!")
|
305
|
+
```
|
306
|
+
|
307
|
+
> `pytest test_get_swag.py`
|
308
|
+
|
309
|
+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
|
310
|
+
|
311
|
+
> (The default browser is ``--chrome`` if not set.)
|
254
312
|
|
255
313
|
--------
|
256
314
|
|
@@ -260,7 +318,7 @@ pytest my_first_test.py
|
|
260
318
|
pytest test_coffee_cart.py --demo
|
261
319
|
```
|
262
320
|
|
263
|
-
<p align="left"><a href="https://seleniumbase.io/coffee/" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/coffee_cart.gif" width="
|
321
|
+
<p align="left"><a href="https://seleniumbase.io/coffee/" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/coffee_cart.gif" width="480" alt="SeleniumBase Coffee Cart Test" title="SeleniumBase Coffee Cart Test" /></a></p>
|
264
322
|
|
265
323
|
> <p>(<code translate="no">--demo</code> mode slows down tests and highlights actions)</p>
|
266
324
|
|
@@ -274,7 +332,7 @@ pytest test_coffee_cart.py --demo
|
|
274
332
|
pytest test_demo_site.py
|
275
333
|
```
|
276
334
|
|
277
|
-
<p align="left"><a href="https://seleniumbase.io/demo_page" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/demo_page_5.gif" width="
|
335
|
+
<p align="left"><a href="https://seleniumbase.io/demo_page" target="_blank"><img src="https://seleniumbase.github.io/cdn/gif/demo_page_5.gif" width="480" alt="SeleniumBase Example" title="SeleniumBase Example" /></a></p>
|
278
336
|
|
279
337
|
> Easy to type, click, select, toggle, drag & drop, and more.
|
280
338
|
|
@@ -344,7 +402,7 @@ With raw Selenium, that requires more code:<br />
|
|
344
402
|
|
345
403
|
<p>📚 <b>Learn about different ways of writing tests:</b></p>
|
346
404
|
|
347
|
-
<p align="left"
|
405
|
+
<p align="left">📗📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py">test_simple_login.py</a>, which uses <code translate="no"><a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py">BaseCase</a></code> class inheritance, and runs with <a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a> or <a href="https://github.com/mdmintz/pynose">pynose</a>. (Use <code translate="no">self.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
|
348
406
|
|
349
407
|
```python
|
350
408
|
from seleniumbase import BaseCase
|
@@ -363,22 +421,7 @@ class TestSimpleLogin(BaseCase):
|
|
363
421
|
self.assert_text("signed out", "#top_message")
|
364
422
|
```
|
365
423
|
|
366
|
-
<p align="left"
|
367
|
-
|
368
|
-
```python
|
369
|
-
def test_sb_fixture_with_no_class(sb):
|
370
|
-
sb.open("seleniumbase.io/simple/login")
|
371
|
-
sb.type("#username", "demo_user")
|
372
|
-
sb.type("#password", "secret_pass")
|
373
|
-
sb.click('a:contains("Sign in")')
|
374
|
-
sb.assert_exact_text("Welcome!", "h1")
|
375
|
-
sb.assert_element("img#image1")
|
376
|
-
sb.highlight("#image1")
|
377
|
-
sb.click_link("Sign out")
|
378
|
-
sb.assert_text("signed out", "#top_message")
|
379
|
-
```
|
380
|
-
|
381
|
-
<p align="left">📙📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_sb.py">raw_login_sb.py</a>, which uses the <b><code translate="no">SB</code></b> Context Manager. Runs with pure <code translate="no">python</code>. (Use <code translate="no">sb.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
|
424
|
+
<p align="left">📘📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_sb.py">raw_login_sb.py</a>, which uses the <b><code translate="no">SB</code></b> Context Manager. Runs with pure <code translate="no">python</code>. (Use <code translate="no">sb.driver</code> to access Selenium's raw <code translate="no">driver</code>.)</p>
|
382
425
|
|
383
426
|
```python
|
384
427
|
from seleniumbase import SB
|
@@ -395,24 +438,7 @@ with SB() as sb:
|
|
395
438
|
sb.assert_text("signed out", "#top_message")
|
396
439
|
```
|
397
440
|
|
398
|
-
<p align="left"
|
399
|
-
|
400
|
-
```python
|
401
|
-
from seleniumbase import DriverContext
|
402
|
-
|
403
|
-
with DriverContext() as driver:
|
404
|
-
driver.open("seleniumbase.io/simple/login")
|
405
|
-
driver.type("#username", "demo_user")
|
406
|
-
driver.type("#password", "secret_pass")
|
407
|
-
driver.click('a:contains("Sign in")')
|
408
|
-
driver.assert_exact_text("Welcome!", "h1")
|
409
|
-
driver.assert_element("img#image1")
|
410
|
-
driver.highlight("#image1")
|
411
|
-
driver.click_link("Sign out")
|
412
|
-
driver.assert_text("signed out", "#top_message")
|
413
|
-
```
|
414
|
-
|
415
|
-
<p align="left">📔📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_driver.py">raw_login_driver.py</a>, which uses the <b><code translate="no">Driver</code></b> Manager. Runs with pure <code translate="no">python</code>. (The <code>driver</code> is an improved version of Selenium's raw <code translate="no">driver</code>, with more methods.)</p>
|
441
|
+
<p align="left">📙📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_login_driver.py">raw_login_driver.py</a>, which uses the <b><code translate="no">Driver</code></b> Manager. Runs with pure <code translate="no">python</code>. (The <code>driver</code> is an improved version of Selenium's raw <code translate="no">driver</code>, with more methods.)</p>
|
416
442
|
|
417
443
|
```python
|
418
444
|
from seleniumbase import Driver
|
@@ -432,23 +458,6 @@ finally:
|
|
432
458
|
driver.quit()
|
433
459
|
```
|
434
460
|
|
435
|
-
<p align="left">📕📝 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/features/login_app.feature">login_app.feature</a>, which uses <a translate="no" href="https://behave.readthedocs.io/en/stable/gherkin.html#features" target="_blank">behave-BDD Gherkin</a> syntax. Runs with <code translate="no">behave</code>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md">Learn about the <b>SeleniumBase behave-BDD</b> integration</a>)</p>
|
436
|
-
|
437
|
-
```gherkin
|
438
|
-
Feature: SeleniumBase scenarios for the Simple App
|
439
|
-
|
440
|
-
Scenario: Verify the Simple App (Login / Logout)
|
441
|
-
Given Open "seleniumbase.io/simple/login"
|
442
|
-
And Type "demo_user" into "#username"
|
443
|
-
And Type "secret_pass" into "#password"
|
444
|
-
And Click 'a:contains("Sign in")'
|
445
|
-
And Assert exact text "Welcome!" in "h1"
|
446
|
-
And Assert element "img#image1"
|
447
|
-
And Highlight "#image1"
|
448
|
-
And Click link "Sign out"
|
449
|
-
And Assert text "signed out" in "#top_message"
|
450
|
-
```
|
451
|
-
|
452
461
|
--------
|
453
462
|
|
454
463
|
<a id="python_installation"></a>
|
@@ -547,20 +556,21 @@ pip install -e .
|
|
547
556
|
<summary> ▶️ Here's sample output from a chromedriver download. (<b>click to expand</b>)</summary>
|
548
557
|
|
549
558
|
```bash
|
550
|
-
*** chromedriver to download =
|
559
|
+
*** chromedriver to download = 131.0.6778.108 (Latest Stable)
|
551
560
|
|
552
561
|
Downloading chromedriver-mac-arm64.zip from:
|
553
|
-
https://storage.googleapis.com/chrome-for-testing-public/
|
562
|
+
https://storage.googleapis.com/chrome-for-testing-public/131.0.6778.108/mac-arm64/chromedriver-mac-arm64.zip ...
|
554
563
|
Download Complete!
|
555
564
|
|
556
565
|
Extracting ['chromedriver'] from chromedriver-mac-arm64.zip ...
|
557
566
|
Unzip Complete!
|
558
567
|
|
559
568
|
The file [chromedriver] was saved to:
|
560
|
-
|
569
|
+
~/github/SeleniumBase/seleniumbase/drivers/
|
570
|
+
chromedriver
|
561
571
|
|
562
|
-
Making [chromedriver
|
563
|
-
[chromedriver
|
572
|
+
Making [chromedriver 131.0.6778.108] executable ...
|
573
|
+
[chromedriver 131.0.6778.108] is now ready for use!
|
564
574
|
```
|
565
575
|
|
566
576
|
</details>
|
@@ -578,9 +588,9 @@ cd examples/
|
|
578
588
|
pytest my_first_test.py
|
579
589
|
```
|
580
590
|
|
581
|
-
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="
|
591
|
+
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py"><img src="https://seleniumbase.github.io/cdn/gif/fast_swag_2.gif" alt="SeleniumBase Test" title="SeleniumBase Test" width="480" /></a>
|
582
592
|
|
583
|
-
<p align="left"><b>Here's the code for <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py">my_first_test.py</a>:</b></p>
|
593
|
+
<p align="left"><b>Here's the full code for <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/my_first_test.py">my_first_test.py</a>:</b></p>
|
584
594
|
|
585
595
|
```python
|
586
596
|
from seleniumbase import BaseCase
|
@@ -996,7 +1006,7 @@ You can run it from the ``examples/`` folder like this:
|
|
996
1006
|
pytest test_fail.py
|
997
1007
|
```
|
998
1008
|
|
999
|
-
🔵 You'll notice that a logs folder,
|
1009
|
+
🔵 You'll notice that a logs folder, ``./latest_logs/``, was created to hold information (and screenshots) about the failing test. During test runs, past results get moved to the archived_logs folder if you have ARCHIVE_EXISTING_LOGS set to True in [settings.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py), or if your run tests with ``--archive-logs``. If you choose not to archive existing logs, they will be deleted and replaced by the logs of the latest test run.
|
1000
1010
|
|
1001
1011
|
--------
|
1002
1012
|
|
@@ -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=0OZXpruceiqE15CL_lL99PZVeEgaBkTbwpFG6D-9WT4,47
|
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=nQ8-5dvNU4Uy5lSTgnwB2nFAemS43PqJhHccdvdGJ94,224486
|
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=SW8wx2f2HfU3ERbANjxEC-jDbjy_IzaNYRKPlayfRRI,23442
|
48
48
|
seleniumbase/core/mysql.py,sha256=8Fzj3p5dhtDWfMpFqFYxpSwa9s1UltiHsWJ56_aPOqk,3993
|
49
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=f6l4GUJP-jg-ivukpbwVLyFNvnlzUnXjm5Y2oUw0I88,76028
|
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=gqVohHVlE_5L5Cqe2L24uYrRzvoK-saX8E_Df7_-_3I,7609
|
@@ -65,12 +65,12 @@ 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=xwRyaBUd7jpN_RQvTXabrMVMBUnuG65Bp8KDDrcoWYk,719184
|
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
72
|
seleniumbase/fixtures/js_utils.py,sha256=asZJ0LDFv2BPQOzUVKK_9ie8ToGcmFousDnGdb3Vabg,51534
|
73
|
-
seleniumbase/fixtures/page_actions.py,sha256=
|
73
|
+
seleniumbase/fixtures/page_actions.py,sha256=LPcFSkUvBkxLrOt4laQHHN-NLmqInT41E2vlPiOlLFY,66753
|
74
74
|
seleniumbase/fixtures/page_utils.py,sha256=5m7iXpikLs80TJoRO6_gEfXE1AKeQgcH1aFbR8o1C9A,12034
|
75
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
|
@@ -114,8 +114,8 @@ seleniumbase/undetected/patcher.py,sha256=n9WfKznr4cQvaE5Gcx7iyK27zMWIc8KdI69_m8
|
|
114
114
|
seleniumbase/undetected/reactor.py,sha256=NropaXcO54pzmDq6quR27qPJxab6636H7LRAaq-o0ng,2859
|
115
115
|
seleniumbase/undetected/webelement.py,sha256=_s6evgUkdWJpwOnzX4qR9i796PoVbz3txlzHlOBJ4BE,1370
|
116
116
|
seleniumbase/undetected/cdp_driver/__init__.py,sha256=c0TjMwPfVFyoqYOJ7PQ-Jln_L_dpN3ebHyaD-juQoM0,64
|
117
|
-
seleniumbase/undetected/cdp_driver/_contradict.py,sha256=
|
118
|
-
seleniumbase/undetected/cdp_driver/browser.py,sha256=
|
117
|
+
seleniumbase/undetected/cdp_driver/_contradict.py,sha256=lP4b0h5quAy573ETn_TBbYV889cL1AuPLVInpJ0ZkiU,3183
|
118
|
+
seleniumbase/undetected/cdp_driver/browser.py,sha256=quD0e2aoehXQrs9zxGsobF0kZY11gmJ58Q_JQhheQYg,30144
|
119
119
|
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=YhtD2Tm6PLIy9VKbgk8lHdGniS3mObyX4yAC1aG0TgQ,16733
|
120
120
|
seleniumbase/undetected/cdp_driver/config.py,sha256=oHFJ3UH0OmLmEGgG5S6SZwbyBs9ZYMsbUJ02QCA7iZc,12044
|
121
121
|
seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
|
@@ -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.12.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
|
139
|
+
seleniumbase-4.33.12.dist-info/METADATA,sha256=6bDBhrptfHbhYpEAgxncG9BFuROdyma7C_CB3UXd8ao,86044
|
140
|
+
seleniumbase-4.33.12.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
141
|
+
seleniumbase-4.33.12.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
|
142
|
+
seleniumbase-4.33.12.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
|
143
|
+
seleniumbase-4.33.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|