Skip to content

Commit 378aad3

Browse files
committed
Add methods to assist with A/B testing
1 parent 3394209 commit 378aad3

File tree

5 files changed

+464
-2
lines changed

5 files changed

+464
-2
lines changed

examples/cdp_mode/ReadMe.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ sb.cdp.wait_for_text_not_visible(text, selector="body", timeout=None)
484484
sb.cdp.wait_for_element_visible(selector, timeout=None)
485485
sb.cdp.wait_for_element_not_visible(selector, timeout=None)
486486
sb.cdp.wait_for_element_absent(selector, timeout=None)
487+
sb.cdp.wait_for_any_of_elements_visible(*args, **kwargs)
488+
sb.cdp.wait_for_any_of_elements_present(*args, **kwargs)
489+
sb.cdp.assert_any_of_elements_visible(*args, **kwargs)
490+
sb.cdp.assert_any_of_elements_present(*args, **kwargs)
487491
sb.cdp.assert_element(selector, timeout=None)
488492
sb.cdp.assert_element_visible(selector, timeout=None)
489493
sb.cdp.assert_element_present(selector, timeout=None)

help_docs/method_summary.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,13 @@ self.assert_elements(*args, **kwargs)
544544

545545
############
546546

547+
self.wait_for_any_of_elements_visible(*args, **kwargs)
548+
self.wait_for_any_of_elements_present(*args, **kwargs)
549+
self.assert_any_of_elements_visible(*args, **kwargs)
550+
self.assert_any_of_elements_present(*args, **kwargs)
551+
552+
############
553+
547554
self.find_text(text, selector="html", by="css selector", timeout=None)
548555
# Duplicates:
549556
# self.wait_for_text(text, selector="html", by="css selector", timeout=None)

seleniumbase/core/sb_cdp.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,150 @@ def wait_for_element_absent(self, selector, timeout=None):
20142014
% (selector, timeout, plural)
20152015
)
20162016

2017+
def wait_for_any_of_elements_visible(self, *args, **kwargs):
2018+
"""Waits for at least one of the elements to be visible.
2019+
Returns the first element that is found.
2020+
The input is a list of elements. (Should be CSS selectors)
2021+
Optional kwargs include: "timeout" (used by all selectors).
2022+
Raises an exception if no elements are visible by the timeout.
2023+
Examples:
2024+
sb.cdp.wait_for_any_of_elements_visible("h1", "h2", "h3")
2025+
OR
2026+
sb.cdp.wait_for_any_of_elements_visible(["h1", "h2", "h3"]) """
2027+
selectors = []
2028+
timeout = None
2029+
for kwarg in kwargs:
2030+
if kwarg == "timeout":
2031+
timeout = kwargs["timeout"]
2032+
elif kwarg == "by":
2033+
pass # Autodetected
2034+
elif kwarg == "selector" or kwarg == "selectors":
2035+
selector = kwargs[kwarg]
2036+
if isinstance(selector, str):
2037+
selectors.append(selector)
2038+
elif isinstance(selector, list):
2039+
selectors_list = selector
2040+
for selector in selectors_list:
2041+
if isinstance(selector, str):
2042+
selectors.append(selector)
2043+
else:
2044+
raise Exception('Unknown kwarg: "%s"!' % kwarg)
2045+
if not timeout:
2046+
timeout = settings.SMALL_TIMEOUT
2047+
for arg in args:
2048+
if isinstance(arg, list):
2049+
for selector in arg:
2050+
if isinstance(selector, str):
2051+
selectors.append(selector)
2052+
elif isinstance(arg, str):
2053+
selectors.append(arg)
2054+
if not selectors:
2055+
raise Exception("The selectors list was empty!")
2056+
start_ms = time.time() * 1000.0
2057+
stop_ms = start_ms + (timeout * 1000.0)
2058+
any_present = False
2059+
for i in range(int(timeout * 10)):
2060+
for selector in selectors:
2061+
if self.is_element_visible(selector):
2062+
return self.select(selector)
2063+
if self.is_element_present(selector):
2064+
any_present = True
2065+
now_ms = time.time() * 1000.0
2066+
if now_ms >= stop_ms:
2067+
break
2068+
time.sleep(0.1)
2069+
plural = "s"
2070+
if timeout == 1:
2071+
plural = ""
2072+
if not any_present:
2073+
# None of the elements exist in the HTML
2074+
raise Exception(
2075+
"None of the elements {%s} were present after %s second%s!" % (
2076+
str(selectors),
2077+
timeout,
2078+
plural,
2079+
)
2080+
)
2081+
raise Exception(
2082+
"None of the elements %s were visible after %s second%s!" % (
2083+
str(selectors),
2084+
timeout,
2085+
plural,
2086+
)
2087+
)
2088+
2089+
def wait_for_any_of_elements_present(self, *args, **kwargs):
2090+
"""Waits for at least one of the elements to be present.
2091+
Visibility not required, but element must be in the DOM.
2092+
Returns the first element that is found.
2093+
The input is a list of elements. (Should be CSS selectors)
2094+
Optional kwargs include: "timeout" (used by all selectors).
2095+
Raises an exception if no elements are present by the timeout.
2096+
Examples:
2097+
self.wait_for_any_of_elements_present("style", "script")
2098+
OR
2099+
self.wait_for_any_of_elements_present(["style", "script"]) """
2100+
selectors = []
2101+
timeout = None
2102+
for kwarg in kwargs:
2103+
if kwarg == "timeout":
2104+
timeout = kwargs["timeout"]
2105+
elif kwarg == "by":
2106+
pass # Autodetected
2107+
elif kwarg == "selector" or kwarg == "selectors":
2108+
selector = kwargs[kwarg]
2109+
if isinstance(selector, str):
2110+
selectors.append(selector)
2111+
elif isinstance(selector, list):
2112+
selectors_list = selector
2113+
for selector in selectors_list:
2114+
if isinstance(selector, str):
2115+
selectors.append(selector)
2116+
else:
2117+
raise Exception('Unknown kwarg: "%s"!' % kwarg)
2118+
if not timeout:
2119+
timeout = settings.SMALL_TIMEOUT
2120+
for arg in args:
2121+
if isinstance(arg, list):
2122+
for selector in arg:
2123+
if isinstance(selector, str):
2124+
selectors.append(selector)
2125+
elif isinstance(arg, str):
2126+
selectors.append(arg)
2127+
if not selectors:
2128+
raise Exception("The selectors list was empty!")
2129+
start_ms = time.time() * 1000.0
2130+
stop_ms = start_ms + (timeout * 1000.0)
2131+
for i in range(int(timeout * 10)):
2132+
for selector in selectors:
2133+
if self.is_element_present(selector):
2134+
return self.select(selector)
2135+
now_ms = time.time() * 1000.0
2136+
if now_ms >= stop_ms:
2137+
break
2138+
time.sleep(0.1)
2139+
plural = "s"
2140+
if timeout == 1:
2141+
plural = ""
2142+
# None of the elements exist in the HTML
2143+
raise Exception(
2144+
"None of the elements %s were present after %s second%s!" % (
2145+
str(selectors),
2146+
timeout,
2147+
plural,
2148+
)
2149+
)
2150+
2151+
def assert_any_of_elements_visible(self, *args, **kwargs):
2152+
"""Like wait_for_any_of_elements_visible(), but returns nothing."""
2153+
self.wait_for_any_of_elements_visible(*args, **kwargs)
2154+
return True
2155+
2156+
def assert_any_of_elements_present(self, *args, **kwargs):
2157+
"""Like wait_for_any_of_elements_present(), but returns nothing."""
2158+
self.wait_for_any_of_elements_present(*args, **kwargs)
2159+
return True
2160+
20172161
def assert_element(self, selector, timeout=None):
20182162
"""Same as assert_element_visible()"""
20192163
self.assert_element_visible(selector, timeout=timeout)

seleniumbase/fixtures/base_case.py

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9227,6 +9227,162 @@ def wait_for_element_not_present(
92279227
original_selector=original_selector,
92289228
)
92299229

9230+
def wait_for_any_of_elements_visible(self, *args, **kwargs):
9231+
"""Waits for at least one of the elements to be visible.
9232+
Returns the first element that is found.
9233+
The input is a list of elements. (Should be CSS selectors or XPath)
9234+
Optional kwargs include: "timeout" (used by all selectors).
9235+
Raises an exception if no elements are visible by the timeout.
9236+
Allows flexible inputs (Eg. Multiple args or a list of args)
9237+
Examples:
9238+
self.wait_for_any_of_elements_visible("h1", "h2", "h3")
9239+
OR
9240+
self.wait_for_any_of_elements_visible(["h1", "h2", "h3"]) """
9241+
self.__check_scope()
9242+
selectors = []
9243+
timeout = None
9244+
for kwarg in kwargs:
9245+
if kwarg == "timeout":
9246+
timeout = kwargs["timeout"]
9247+
elif kwarg == "by":
9248+
pass # Autodetected
9249+
elif kwarg == "selector" or kwarg == "selectors":
9250+
selector = kwargs[kwarg]
9251+
if isinstance(selector, str):
9252+
selectors.append(selector)
9253+
elif isinstance(selector, list):
9254+
selectors_list = selector
9255+
for selector in selectors_list:
9256+
if isinstance(selector, str):
9257+
selectors.append(selector)
9258+
else:
9259+
raise Exception('Unknown kwarg: "%s"!' % kwarg)
9260+
if not timeout:
9261+
timeout = settings.LARGE_TIMEOUT
9262+
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
9263+
timeout = self.__get_new_timeout(timeout)
9264+
for arg in args:
9265+
if isinstance(arg, list):
9266+
for selector in arg:
9267+
if isinstance(selector, str):
9268+
selectors.append(selector)
9269+
elif isinstance(arg, str):
9270+
selectors.append(arg)
9271+
if not selectors:
9272+
raise Exception("The selectors list was empty!")
9273+
original_selectors = selectors
9274+
updated_selectors = []
9275+
for selector in selectors:
9276+
by = "css selector"
9277+
if page_utils.is_xpath_selector(selector):
9278+
by = "xpath"
9279+
selector, by = self.__recalculate_selector(selector, by)
9280+
updated_selectors.append(selector)
9281+
selectors = updated_selectors
9282+
if self.__is_cdp_swap_needed():
9283+
return self.cdp.wait_for_any_of_elements_visible(
9284+
selectors, timeout=timeout
9285+
)
9286+
return page_actions.wait_for_any_of_elements_visible(
9287+
self.driver,
9288+
selectors,
9289+
timeout=timeout,
9290+
original_selectors=original_selectors,
9291+
)
9292+
9293+
def wait_for_any_of_elements_present(self, *args, **kwargs):
9294+
"""Waits for at least one of the elements to be present.
9295+
Visibility not required, but element must be in the DOM.
9296+
Returns the first element that is found.
9297+
The input is a list of elements. (Should be CSS selectors or XPath)
9298+
Optional kwargs include: "timeout" (used by all selectors).
9299+
Raises an exception if no elements are present by the timeout.
9300+
Allows flexible inputs (Eg. Multiple args or a list of args)
9301+
Examples:
9302+
self.wait_for_any_of_elements_present("style", "script")
9303+
OR
9304+
self.wait_for_any_of_elements_present(["style", "script"]) """
9305+
self.__check_scope()
9306+
selectors = []
9307+
timeout = None
9308+
for kwarg in kwargs:
9309+
if kwarg == "timeout":
9310+
timeout = kwargs["timeout"]
9311+
elif kwarg == "by":
9312+
pass # Autodetected
9313+
elif kwarg == "selector" or kwarg == "selectors":
9314+
selector = kwargs[kwarg]
9315+
if isinstance(selector, str):
9316+
selectors.append(selector)
9317+
elif isinstance(selector, list):
9318+
selectors_list = selector
9319+
for selector in selectors_list:
9320+
if isinstance(selector, str):
9321+
selectors.append(selector)
9322+
else:
9323+
raise Exception('Unknown kwarg: "%s"!' % kwarg)
9324+
if not timeout:
9325+
timeout = settings.LARGE_TIMEOUT
9326+
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
9327+
timeout = self.__get_new_timeout(timeout)
9328+
for arg in args:
9329+
if isinstance(arg, list):
9330+
for selector in arg:
9331+
if isinstance(selector, str):
9332+
selectors.append(selector)
9333+
elif isinstance(arg, str):
9334+
selectors.append(arg)
9335+
if not selectors:
9336+
raise Exception("The selectors list was empty!")
9337+
original_selectors = selectors
9338+
updated_selectors = []
9339+
for selector in selectors:
9340+
by = "css selector"
9341+
if page_utils.is_xpath_selector(selector):
9342+
by = "xpath"
9343+
selector, by = self.__recalculate_selector(selector, by)
9344+
updated_selectors.append(selector)
9345+
selectors = updated_selectors
9346+
if self.__is_cdp_swap_needed():
9347+
return self.cdp.wait_for_any_of_elements_present(
9348+
selectors, timeout=timeout
9349+
)
9350+
return page_actions.wait_for_any_of_elements_present(
9351+
self.driver,
9352+
selectors,
9353+
timeout=timeout,
9354+
original_selectors=original_selectors,
9355+
)
9356+
9357+
def assert_any_of_elements_visible(self, *args, **kwargs):
9358+
"""Similar to wait_for_any_of_elements_visible(), but returns nothing.
9359+
As above, raises an exception if none of the set elements are visible.
9360+
Returns True if successful. Default timeout = SMALL_TIMEOUT.
9361+
Allows flexible inputs (Eg. Multiple args or a list of args)
9362+
Examples:
9363+
self.assert_any_of_elements_visible("h1", "h2", "h3")
9364+
OR
9365+
self.assert_any_of_elements_visible(["h1", "h2", "h3"]) """
9366+
if "timeout" not in kwargs:
9367+
kwargs["timeout"] = settings.SMALL_TIMEOUT
9368+
self.wait_for_any_of_elements_visible(*args, **kwargs)
9369+
return True
9370+
9371+
def assert_any_of_elements_present(self, *args, **kwargs):
9372+
"""Similar to wait_for_any_of_elements_present(), but returns nothing.
9373+
As above, raises an exception if none of the given elements are found.
9374+
Visibility is not required, but element must exist in the DOM.
9375+
Returns True if successful. Default timeout = SMALL_TIMEOUT.
9376+
Allows flexible inputs (Eg. Multiple args or a list of args)
9377+
Examples:
9378+
self.assert_any_of_elements_present("h1", "h2", "h3")
9379+
OR
9380+
self.assert_any_of_elements_present(["h1", "h2", "h3"]) """
9381+
if "timeout" not in kwargs:
9382+
kwargs["timeout"] = settings.SMALL_TIMEOUT
9383+
self.wait_for_any_of_elements_present(*args, **kwargs)
9384+
return True
9385+
92309386
def select_all(self, selector, by="css selector", limit=0):
92319387
return self.find_elements(selector, by=by, limit=limit)
92329388

@@ -9698,6 +9854,7 @@ def assert_elements_present(self, *args, **kwargs):
96989854
The input is a list of elements.
96999855
Optional kwargs include "by" and "timeout" (used by all selectors).
97009856
Raises an exception if any of the elements are not visible.
9857+
Allows flexible inputs (Eg. Multiple args or a list of args)
97019858
Examples:
97029859
self.assert_elements_present("head", "style", "script", "body")
97039860
OR
@@ -9711,7 +9868,7 @@ def assert_elements_present(self, *args, **kwargs):
97119868
timeout = kwargs["timeout"]
97129869
elif kwarg == "by":
97139870
by = kwargs["by"]
9714-
elif kwarg == "selector":
9871+
elif kwarg == "selector" or kwarg == "selectors":
97159872
selector = kwargs["selector"]
97169873
if isinstance(selector, str):
97179874
selectors.append(selector)
@@ -9804,6 +9961,7 @@ def assert_elements(self, *args, **kwargs):
98049961
The input is a list of elements.
98059962
Optional kwargs include "by" and "timeout" (used by all selectors).
98069963
Raises an exception if any of the elements are not visible.
9964+
Allows flexible inputs (Eg. Multiple args or a list of args)
98079965
Examples:
98089966
self.assert_elements("h1", "h2", "h3")
98099967
OR
@@ -9817,7 +9975,7 @@ def assert_elements(self, *args, **kwargs):
98179975
timeout = kwargs["timeout"]
98189976
elif kwarg == "by":
98199977
by = kwargs["by"]
9820-
elif kwarg == "selector":
9978+
elif kwarg == "selector" or kwarg == "selectors":
98219979
selector = kwargs["selector"]
98229980
if isinstance(selector, str):
98239981
selectors.append(selector)

0 commit comments

Comments
 (0)