Merge pull request #3124 from seleniumbase/refactor-uc-mode-and-js-waits

Refactor UC Mode and JS waits
This commit is contained in:
Michael Mintz 2024-09-12 01:08:10 -04:00 committed by GitHub
commit 6a057913b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 102 additions and 38 deletions

View File

@ -651,6 +651,7 @@ pytest test_coffee_cart.py --trace
--binary-location=PATH # (Set path of the Chromium browser binary to use.)
--driver-version=VER # (Set the chromedriver or uc_driver version to use.)
--sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.)
--wfa # (Wait for AngularJS to be done loading after specific web actions.)
--pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
--headless # (Run tests in headless mode. The default arg on Linux OS.)
--headless2 # (Use the new headless mode, which supports extensions.)

View File

@ -48,7 +48,7 @@ WAIT_FOR_RSC_ON_PAGE_LOADS = True
# Called after self.click(selector), NOT element.click()
WAIT_FOR_RSC_ON_CLICKS = False
# Wait for AngularJS calls to complete after various browser actions.
WAIT_FOR_ANGULARJS = True
WAIT_FOR_ANGULARJS = False
# Skip ALL calls to wait_for_ready_state_complete() and wait_for_angularjs().
SKIP_JS_WAITS = False

View File

@ -131,10 +131,11 @@ class HackTests(BaseCase):
self.highlight("section.crayons-card", loops=7, scroll=False)
self.open("https://azure.microsoft.com/en-us/services/playfab/")
self.remove_elements('div[role="dialog"]')
self.set_text_content("h1", aybabtu)
self.set_text_content('a[aria-label*="Try Azure"]', ayb)
self.set_text_content('a[aria-label*="Sign in to"]', abtu)
self.remove_elements('div[role="dialog"]')
self.remove_elements('[aria-label*="Microsoft Survey"]')
self.highlight("h1", loops=6, scroll=False)
self.highlight('a[aria-label*="Try Azure"]', loops=4, scroll=False)
self.highlight('a[aria-label*="Sign in to"]', loops=6, scroll=False)
@ -308,15 +309,6 @@ class HackTests(BaseCase):
self.highlight("h1", loops=6, scroll=False)
self.highlight("input#search", loops=8, scroll=False)
self.open("https://www.atlassian.com/software/jira")
self.set_text_content('a[href*="jira/pricing"]', ayb)
self.set_text_content('a[href*="jira/enterprise"]', abtu)
self.set_text_content('a[href="/software/jira/features"]', "")
self.set_text_content("h1", aybabtu)
self.highlight('a[href*="jira/pricing"]', loops=5, scroll=False)
self.highlight('a[href*="jira/enterprise"]', loops=6, scroll=False)
self.highlight("h1", loops=8, scroll=False)
self.open("https://status.iboss.com/ibcloud/app/cloudStatus.html")
self.wait_for_element_clickable('div[translate*="cloudStatus"]')
self.set_text_content('div[translate*="cloudStatus"]', ayb)

View File

@ -7,9 +7,9 @@ with SB(uc=True, test=True, incognito=True, locale_code="en") as sb:
sb.uc_open_with_reconnect(url) # The bot-check is later
sb.type(input_field, "github.com/seleniumbase/SeleniumBase")
sb.reconnect(0.1)
sb.uc_click(submit_button, reconnect_time=4)
sb.uc_click(submit_button, reconnect_time=3.25)
sb.uc_gui_click_captcha()
sb.wait_for_text_not_visible("Checking", timeout=12)
sb.wait_for_text_not_visible("Checking", timeout=11.5)
sb.highlight('p:contains("github.com/seleniumbase/SeleniumBase")')
sb.highlight('a:contains("Top 100 backlinks")')
sb.set_messenger_theme(location="bottom_center")

View File

@ -2,6 +2,7 @@ from seleniumbase import SB
with SB(uc=True, test=True) as sb:
sb.uc_open_with_reconnect("nopecha.com/demo/turnstile", 3.2)
sb.uc_gui_click_captcha("#example-container0")
if sb.is_element_visible("#example-container0"):
sb.uc_gui_click_captcha("#example-container0")
sb.uc_gui_click_captcha("#example-container5")
sb.sleep(3)

View File

@ -39,7 +39,7 @@ class TestGeolocation(BaseCase):
)
self.open("https://www.openstreetmap.org/")
self.click("span.geolocate")
self.assert_url_contains("48.87645/2.26340")
self.assert_url_contains("48.876450/2.263400")
self.save_screenshot_to_logs()
if self.headed:
self.sleep(4)

View File

@ -142,6 +142,7 @@ pytest my_first_test.py --settings-file=custom_settings.py
--binary-location=PATH # (Set path of the Chromium browser binary to use.)
--driver-version=VER # (Set the chromedriver or uc_driver version to use.)
--sjw # (Skip JS Waits for readyState to be "complete" or Angular to load.)
--wfa # (Wait for AngularJS to be done loading after specific web actions.)
--pls=PLS # (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
--headless # (Run tests in headless mode. The default arg on Linux OS.)
--headless2 # (Use the new headless mode, which supports extensions.)

View File

@ -1,7 +1,7 @@
# mkdocs dependencies for generating the seleniumbase.io website
# Minimum Python version: 3.8 (for generating docs only)
regex>=2024.7.24
regex>=2024.9.11
pymdown-extensions>=10.9
pipdeptree>=2.23.3
python-dateutil>=2.8.2

View File

@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.30.4"
__version__ = "4.30.5"

View File

@ -42,6 +42,7 @@ behave -D agent="User Agent String" -D demo
-D binary-location=PATH (Set path of the Chromium browser binary to use.)
-D driver-version=VER (Set the chromedriver or uc_driver version to use.)
-D sjw (Skip JS Waits for readyState to be "complete" or Angular to load.)
-D wfa (Wait for AngularJS to be done loading after specific web actions.)
-D pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
-D headless (Run tests in headless mode. The default arg on Linux OS.)
-D headless2 (Use the new headless mode, which supports extensions.)
@ -588,6 +589,10 @@ def get_configured_sb(context):
if low_key in ["sjw", "skip-js-waits", "skip_js_waits"]:
settings.SKIP_JS_WAITS = True
continue
# Handle: -D wfa / wait-for-angularjs / wait_for_angularjs
if low_key in ["wfa", "wait-for-angularjs", "wait_for_angularjs"]:
settings.WAIT_FOR_ANGULARJS = True
continue
# Handle: -D visual-baseline / visual_baseline
if low_key in ["visual-baseline", "visual_baseline"]:
sb.visual_baseline = True
@ -889,6 +894,16 @@ def get_configured_sb(context):
# If the port is "443", the protocol is "https"
if str(sb.port) == "443":
sb.protocol = "https"
if (
(sb.enable_ws is None and sb.disable_ws is None)
or (sb.disable_ws is not None and not sb.disable_ws)
or (sb.enable_ws is not None and sb.enable_ws)
):
sb.enable_ws = True
sb.disable_ws = False
else:
sb.enable_ws = False
sb.disable_ws = True
if sb.window_size:
window_size = sb.window_size
if window_size.count(",") != 1:

View File

@ -75,7 +75,7 @@ WAIT_FOR_RSC_ON_PAGE_LOADS = True
# Called after self.click(selector), NOT element.click()
WAIT_FOR_RSC_ON_CLICKS = False
# Wait for AngularJS calls to complete after various browser actions.
WAIT_FOR_ANGULARJS = True
WAIT_FOR_ANGULARJS = False
# Skip all calls to wait_for_ready_state_complete() and wait_for_angularjs().
SKIP_JS_WAITS = False

View File

@ -982,16 +982,19 @@ def _uc_gui_click_captcha(
_uc_gui_click_x_y(driver, x, y, timeframe=0.95)
except Exception:
pass
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.5
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6
if IS_LINUX:
reconnect_time = constants.UC.RECONNECT_TIME + 0.2
if not x or not y:
reconnect_time = 1 # Make it quick (it already failed)
driver.reconnect(reconnect_time)
if blind or (IS_LINUX and "Just a moment" in driver.title):
retry = True
caught = False
if driver.is_element_present(".footer .clearfix .ray-id"):
blind = True
if retry and x and y and _on_a_captcha_page(driver):
caught = True
if blind:
retry = True
if retry and x and y and (caught or _on_a_captcha_page(driver)):
with gui_lock: # Prevent issues with multiple processes
# Make sure the window is on top
page_actions.switch_to_window(
@ -1056,11 +1059,7 @@ def uc_gui_click_cf(driver, frame="iframe", retry=False, blind=False):
)
def _uc_gui_handle_captcha(
driver,
frame="iframe",
ctype=None,
):
def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
if ctype == "cf_t":
if not _on_a_cf_turnstile_page(driver):
return
@ -1202,12 +1201,19 @@ def _uc_gui_handle_captcha(
pyautogui.press(" ")
except Exception:
pass
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.5
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6
if IS_LINUX:
reconnect_time = constants.UC.RECONNECT_TIME + 0.2
driver.reconnect(reconnect_time)
def _uc_gui_handle_captcha(driver, frame="iframe", ctype=None):
_uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype)
if driver.is_element_present(".footer .clearfix .ray-id"):
driver.uc_open_with_reconnect(driver.current_url, 3.8)
_uc_gui_handle_captcha_(driver, frame=frame, ctype=ctype)
def uc_gui_handle_captcha(driver, frame="iframe"):
_uc_gui_handle_captcha(driver, frame=frame, ctype=None)

View File

@ -16071,7 +16071,10 @@ class BaseCase(unittest.TestCase):
try:
self.driver.window_handles
except Exception:
self.driver.connect()
try:
self.driver.connect()
except Exception:
pass
self.__slow_mode_pause_if_active()
has_exception = self.__has_exception()
sb_config._has_exception = has_exception

View File

@ -48,7 +48,7 @@ def wait_for_ready_state_complete(driver, timeout=settings.LARGE_TIMEOUT):
return False # readyState stayed "interactive" (Not "complete")
def execute_async_script(driver, script, timeout=settings.EXTREME_TIMEOUT):
def execute_async_script(driver, script, timeout=settings.LARGE_TIMEOUT):
driver.set_script_timeout(timeout)
return driver.execute_async_script(script)
@ -56,11 +56,20 @@ def execute_async_script(driver, script, timeout=settings.EXTREME_TIMEOUT):
def wait_for_angularjs(driver, timeout=settings.LARGE_TIMEOUT, **kwargs):
if hasattr(settings, "SKIP_JS_WAITS") and settings.SKIP_JS_WAITS:
return
try:
# This closes pop-up alerts
driver.execute_script("")
except Exception:
pass
if hasattr(driver, "_is_using_uc") and driver._is_using_uc:
# Calling AngularJS waits may make UC Mode detectable.
# Instead, pause for a brief moment, and then return.
time.sleep(0.007)
return
if not settings.WAIT_FOR_ANGULARJS:
return
if timeout == settings.MINI_TIMEOUT:
timeout = settings.MINI_TIMEOUT / 2.0
timeout = settings.MINI_TIMEOUT / 4.0
NG_WRAPPER = (
"%(prefix)s"
"var $elm=document.querySelector("
@ -84,15 +93,10 @@ def wait_for_angularjs(driver, timeout=settings.LARGE_TIMEOUT, **kwargs):
"handler": handler,
"suffix": suffix,
}
try:
# This closes any pop-up alerts (otherwise the next part fails)
driver.execute_script("")
except Exception:
pass
try:
execute_async_script(driver, script, timeout=timeout)
except Exception:
time.sleep(0.05)
time.sleep(0.0456)
def convert_to_css_selector(selector, by=By.CSS_SELECTOR):

View File

@ -59,6 +59,7 @@ def pytest_addoption(parser):
--binary-location=PATH (Set path of the Chromium browser binary to use.)
--driver-version=VER (Set the chromedriver or uc_driver version to use.)
--sjw (Skip JS Waits for readyState to be "complete" or Angular to load.)
--wfa (Wait for AngularJS to be done loading after specific web actions.)
--pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
--headless (Run tests in headless mode. The default arg on Linux OS.)
--headless2 (Use the new headless mode, which supports extensions.)
@ -358,6 +359,17 @@ def pytest_addoption(parser):
and wait_for_angularjs(), which are part of many
SeleniumBase methods for improving reliability.""",
)
parser.addoption(
"--wfa",
"--wait_for_angularjs",
"--wait-for-angularjs",
action="store_true",
dest="wait_for_angularjs",
default=False,
help="""Add waiting for AngularJS. (The default setting
was changed to no longer wait for AngularJS to
finish loading as an extra JavaScript call.)""",
)
parser.addoption(
"--with-db_reporting",
"--with-db-reporting",
@ -1546,6 +1558,8 @@ def pytest_configure(config):
settings.ARCHIVE_EXISTING_DOWNLOADS = True
if config.getoption("skip_js_waits"):
settings.SKIP_JS_WAITS = True
if config.getoption("wait_for_angularjs"):
settings.WAIT_FOR_ANGULARJS = True
sb_config.all_scripts = config.getoption("all_scripts")
sb_config._time_limit = config.getoption("time_limit")
sb_config.time_limit = config.getoption("time_limit")

View File

@ -76,6 +76,7 @@ def SB(
binary_location=None, # Set path of the Chromium browser binary to use.
driver_version=None, # Set the chromedriver or uc_driver version to use.
skip_js_waits=None, # Skip JS Waits (readyState=="complete" and Angular).
wait_for_angularjs=None, # Wait for AngularJS to load after some actions.
use_wire=None, # Use selenium-wire's webdriver over selenium webdriver.
external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True.
is_mobile=None, # Use the mobile device emulator while running tests.
@ -109,6 +110,7 @@ def SB(
wire=None, # Shortcut / Duplicate of "use_wire".
pls=None, # Shortcut / Duplicate of "page_load_strategy".
sjw=None, # Shortcut / Duplicate of "skip_js_waits".
wfa=None, # Shortcut / Duplicate of "wait_for_angularjs".
save_screenshot=None, # Save a screenshot at the end of each test.
no_screenshot=None, # No screenshots saved unless tests directly ask it.
page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none".
@ -607,6 +609,17 @@ def SB(
settings.SKIP_JS_WAITS = True
elif skip_js_waits:
settings.SKIP_JS_WAITS = skip_js_waits
if wfa is not None and wait_for_angularjs is None:
wait_for_angularjs = wfa
if wait_for_angularjs is None:
if (
"--wfa" in sys_argv
or "--wait_for_angularjs" in sys_argv
or "--wait-for-angularjs" in sys_argv
):
settings.WAIT_FOR_ANGULARJS = True
elif wait_for_angularjs:
settings.WAIT_FOR_ANGULARJS = wait_for_angularjs
if save_screenshot is None:
if (
"--screenshot" in sys_argv

View File

@ -40,6 +40,7 @@ class SeleniumBrowser(Plugin):
--binary-location=PATH (Set path of the Chromium browser binary to use.)
--driver-version=VER (Set the chromedriver or uc_driver version to use.)
--sjw (Skip JS Waits for readyState to be "complete" or Angular to load.)
--wfa (Wait for AngularJS to be done loading after specific web actions.)
--pls=PLS (Set pageLoadStrategy on Chrome: "normal", "eager", or "none".)
--headless (Run tests in headless mode. The default arg on Linux OS.)
--headless2 (Use the new headless mode, which supports extensions.)
@ -178,6 +179,17 @@ class SeleniumBrowser(Plugin):
and wait_for_angularjs(), which are part of many
SeleniumBase methods for improving reliability.""",
)
parser.addoption(
"--wfa",
"--wait_for_angularjs",
"--wait-for-angularjs",
action="store_true",
dest="wait_for_angularjs",
default=False,
help="""Add waiting for AngularJS. (The default setting
was changed to no longer wait for AngularJS to
finish loading as an extra JavaScript call.)""",
)
parser.addoption(
"--protocol",
action="store",
@ -1105,6 +1117,8 @@ class SeleniumBrowser(Plugin):
test.test.start_page = self.options.start_page
if self.options.skip_js_waits:
settings.SKIP_JS_WAITS = True
if self.options.wait_for_angularjs:
settings.WAIT_FOR_ANGULARJS = True
test.test.protocol = self.options.protocol
test.test.servername = self.options.servername
test.test.port = self.options.port