Skip to content

Refactor UC Mode and JS waits #3124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
# 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

Expand Down
12 changes: 2 additions & 10 deletions examples/hack_the_planet.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,11 @@ def test_all_your_base_are_belong_to_us(self):
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)
Expand Down Expand Up @@ -308,15 +309,6 @@ def test_all_your_base_are_belong_to_us(self):
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)
Expand Down
4 changes: 2 additions & 2 deletions examples/raw_ahrefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
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")
Expand Down
3 changes: 2 additions & 1 deletion examples/raw_nopecha.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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)
2 changes: 1 addition & 1 deletion examples/test_geolocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_geolocation(self):
)
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)
1 change: 1 addition & 0 deletions help_docs/customizing_test_runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
2 changes: 1 addition & 1 deletion mkdocs_build/requirements.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.30.4"
__version__ = "4.30.5"
15 changes: 15 additions & 0 deletions seleniumbase/behave/behave_sb.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
-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.)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
# 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

Expand Down
26 changes: 16 additions & 10 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
5 changes: 4 additions & 1 deletion seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -16071,7 +16071,10 @@ def tearDown(self):
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
Expand Down
22 changes: 13 additions & 9 deletions seleniumbase/fixtures/js_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,28 @@ 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)


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("
Expand All @@ -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):
Expand Down
14 changes: 14 additions & 0 deletions seleniumbase/plugins/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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")
Expand Down
13 changes: 13 additions & 0 deletions seleniumbase/plugins/sb_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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".
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions seleniumbase/plugins/selenium_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down Expand Up @@ -178,6 +179,17 @@ def options(self, parser, env):
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",
Expand Down Expand Up @@ -1105,6 +1117,8 @@ def beforeTest(self, test):
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
Expand Down