-
+
(For maximum stealth, use CDP Mode, which includes Stealthy Playwright Mode)
@@ -80,6 +80,7 @@ sb = sb_cdp.Chrome(url)
elements = sb.find_elements("span.titleline > a")
for element in elements:
print("* " + element.text)
+sb.driver.stop()
```
--------
@@ -491,6 +492,7 @@ class MyTestClass(BaseCase):
```python
self.open(url) # Navigate the browser window to the URL.
+self.activate_cdp_mode() # Activate CDP Mode from UC Mode.
self.type(selector, text) # Update the field with the text.
self.click(selector) # Click the element with the selector.
self.click_link(link_text) # Click the link containing text.
@@ -579,7 +581,7 @@ pytest [FILE_NAME.py]::[CLASS_NAME]::[METHOD_NAME]
pynose [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME]
```
-
✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit). This means you no longer need random time.sleep() statements in your scripts.
+
✅ No More Flaky Tests! SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit).
✅ SeleniumBase supports all major browsers and operating systems:
diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md
index 830eb00bc33..6ba8a2d2852 100644
--- a/examples/cdp_mode/ReadMe.md
+++ b/examples/cdp_mode/ReadMe.md
@@ -6,19 +6,17 @@
----
-⚙️ This diagram shows the stealthy architecture with CDP Mode:
+
⚙️ Stealthy architecture flowchart:
-
+
----
-### 🎞️ YouTube tutorials that cover CDP Mode:
+### 🎞️ YouTube videos about CDP Mode:
* A powerful Python framework for browser automation and E2E UI testing.
-* Includes [Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md) for instantly generating browser tests in Python.
-* Supports multiple browsers, tabs, iframes, and proxies in the same test.
-* Includes [Test Case Management Software](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/case_plans.md) with Markdown technology.
+* Has [CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) for bypassing bot-detection. (`activate_cdp_mode(url)`)
+* [Stealthy Playwright Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/playwright/ReadMe.md) lets you bypass bot-detection with Playwright.
+* There's [Recorder Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/recorder_mode.md) for instantly generating browser tests in Python.
+* Supports [pytest](https://docs.pytest.org/en/latest/), [unittest](https://docs.python.org/3/library/unittest.html), [nose](http://nose.readthedocs.io/en/latest/), and [behave](https://behave.readthedocs.io/en/stable/index.html) for finding & running tests.
* Automatic smart-waiting improves reliability and prevents flaky tests.
-* Supports [pytest](https://docs.pytest.org/en/latest/), [unittest](https://docs.python.org/3/library/unittest.html), [nose](http://nose.readthedocs.io/en/latest/), and [behave](https://behave.readthedocs.io/en/stable/index.html) for finding/running tests.
* All the code is open source. Look inside to learn about any feature.
* Powerful logging tools for [dashboards, reports, and screenshots](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/example_logs/ReadMe.md).
-* Can run tests in Headless Mode to hide the browser. (``--headless``)
-* Can run tests multithreaded from parallel browsers. (``-n NUM_THREADS``)
-* Can run tests from a shared browser session. (``--reuse-session``/``--rs``)
-* Can run tests using [Chromium's mobile device emulator](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/mobile_testing.md). (``--mobile``)
-* Can run tests through a proxy server. (``--proxy=IP_ADDRESS:PORT``)
-* Can run tests with proxy settings via PAC URL. (``--proxy-pac-url=URL.pac``)
-* Can run tests through an authenticated proxy server. (``--proxy=USER:PASS@HOST:PORT``)
-* Can run tests with proxy+auth via PAC URL. (``--proxy-pac-url=USER:PASS@URL.pac``)
-* Can run tests with a customized browser user agent. (``--agent=USER_AGENT_STRING``)
-* Can set a Chromium User Data Directory/Profile to load. (``--user-data-dir=DIR``)
-* Can [avoid detection](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/uc_mode.md) by sites that try to block Selenium. (``--undetected``/``--uc``)
-* Can [use CDP Mode](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/cdp_mode/ReadMe.md) for advanced stealth abilities. (``activate_cdp_mode(url)``)
-* Can integrate with [selenium-wire](https://github.com/wkeeling/selenium-wire) for inspecting browser requests. (``--wire``)
-* Can load Chrome Extension ZIP files. (``--extension-zip=ZIP``)
-* Can load Chrome Extension folders. (``--extension-dir=DIR``)
-* Powerful [console scripts](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md). (Type **``seleniumbase``** or **``sbase``** to use.)
+* Can run tests in Headless Mode to hide the browser. (`--headless`)
+* Can run tests multithreaded from parallel browsers. (`-n NUM_THREADS`)
+* Can run tests from a shared browser session. (`--reuse-session`/`--rs`)
+* Can run tests using [Chromium's mobile device emulator](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/mobile_testing.md). (`--mobile`)
+* Can run tests through a proxy server. (`--proxy=IP_ADDRESS:PORT`)
+* Can run tests with proxy settings via PAC URL. (`--proxy-pac-url=URL.pac`)
+* Can run tests through an authenticated proxy server. (`--proxy=USER:PASS@HOST:PORT`)
+* Can run tests with proxy+auth via PAC URL. (`--proxy-pac-url=USER:PASS@URL.pac`)
+* Can run tests with a customized browser user agent. (`--agent=USER_AGENT_STRING`)
+* Can set a Chromium User Data Directory to load. (`--user-data-dir=DIR`)
+* Can load Chromium Extension folders. (`--extension-dir=DIR`)
+* Powerful [console scripts](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md). (Type **`seleniumbase`** or **`sbase`** to use.)
* Has the ability to translate tests into [multiple spoken languages](https://github.com/seleniumbase/SeleniumBase/tree/master/examples/translations).
* Has a flexible [command-line interface](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) for customizing test runs.
* Has a [global config file](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/config/settings.py) for configuring settings as needed.
@@ -43,19 +36,13 @@
* Can handle Google Authenticator logins with [Python's one-time password library](https://pyotp.readthedocs.io/en/latest/).
* Can load and make assertions on PDF files from websites or the local file system.
* Can inspect HTML to find issues and points of interest with the [HTML Inspector](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/html_inspector.md).
-* Is backwards-compatible with Python [WebDriver](https://www.selenium.dev/projects/) methods. (Use: ``self.driver``)
-* Can execute JavaScript code from Python calls. (Use: ``self.execute_script()``)
-* Can pierce through [Shadow DOM selectors](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/shadow_dom.md). (Add ``::shadow`` to CSS fragments.)
+* Is backwards-compatible with Python [WebDriver](https://www.selenium.dev/projects/) methods. (Use: `self.driver`)
+* Can execute JavaScript code from Python calls. (Use: `self.execute_script()`)
+* Can pierce through [Shadow DOM selectors](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/shadow_dom.md). (Add `::shadow` to CSS fragments.)
* Includes a hybrid-automation solution, [MasterQA](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/masterqa/ReadMe.md), to speed up manual testing.
+* Includes [Test Case Management Software](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/case_plans.md) with Markdown technology.
* Includes useful [Python decorators and password obfuscation methods](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/common/ReadMe.md).
--------
-
-
-
-
+
diff --git a/help_docs/how_it_works.md b/help_docs/how_it_works.md
index ff5f6765f70..6a68889afc3 100644
--- a/help_docs/how_it_works.md
+++ b/help_docs/how_it_works.md
@@ -6,27 +6,27 @@
👁️🔎 The primary [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) works by extending [pytest](https://docs.pytest.org/en/latest/) as a direct plugin. SeleniumBase automatically spins up web browsers for tests, and then gives those tests access to the SeleniumBase libraries through the [BaseCase class](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py). Tests are also given access to [SeleniumBase command-line options](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md) and [SeleniumBase methods](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md).
-👁️🔎 ``pytest`` uses a feature called test discovery to automatically find and run Python methods that start with ``test_`` when those methods are located in Python files that start with ``test_`` or end with ``_test.py``.
+👁️🔎 `pytest` uses a feature called test discovery to automatically find and run Python methods that start with `test_` when those methods are located in Python files that start with `test_` or end with `_test.py`/`_tests.py`.
-👁️🔎 The primary [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) starts by importing ``BaseCase``:
+👁️🔎 The primary [SeleniumBase syntax format](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/syntax_formats.md) starts by importing `BaseCase`:
```python
from seleniumbase import BaseCase
```
-👁️🔎 This next line activates ``pytest`` when a file is called directly with ``python`` by accident:
+👁️🔎 This next line activates `pytest` when a file is called directly with `python` by accident:
```python
BaseCase.main(__name__, __file__)
```
-👁️🔎 Classes can inherit ``BaseCase`` to gain SeleniumBase functionality:
+👁️🔎 Classes can inherit `BaseCase` to gain SeleniumBase functionality:
```python
class MyTestClass(BaseCase):
```
-👁️🔎 Test methods inside ``BaseCase`` classes become SeleniumBase tests: (These tests automatically launch a web browser before starting, and quit the web browser after ending. Default settings can be changed via command-line options.)
+👁️🔎 Test methods inside `BaseCase` classes become SeleniumBase tests: (These tests automatically launch a web browser before starting, and quit the web browser after ending. Default settings can be changed via command-line options.)
```python
class MyTestClass(BaseCase):
@@ -34,7 +34,7 @@ class MyTestClass(BaseCase):
# ...
```
-👁️🔎 [SeleniumBase APIs](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md) can be called from tests via ``self``:
+👁️🔎 [SeleniumBase APIs](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/method_summary.md) can be called from tests via `self`:
```python
class MyTestClass(BaseCase):
@@ -63,7 +63,7 @@ class TestSimpleLogin(BaseCase):
(See the example, [test_simple_login.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_simple_login.py), for reference.)
-👁️🔎 Here are some examples of running tests with ``pytest``:
+👁️🔎 Here are some examples of running tests with `pytest`:
```zsh
pytest test_mfa_login.py
@@ -101,22 +101,22 @@ finally:
### ✅ No More Flaky Tests!
-
SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit). This means you no longer need random time.sleep() statements in your scripts.
+
SeleniumBase methods automatically wait for page elements to finish loading before interacting with them (up to a timeout limit).
**There are three layers of protection that provide reliability for tests using SeleniumBase:**
-* **(1)**: Selenium's default ``pageLoadStrategy`` is ``normal``: This strategy causes Selenium to wait for the full page to load, with HTML content and sub-resources downloaded and parsed.
+* **(1)**: Selenium's default `pageLoadStrategy` is `normal`: This strategy causes Selenium to wait for the full page to load, with HTML content and sub-resources downloaded and parsed.
-* **(2)**: SeleniumBase includes methods such as ``wait_for_ready_state_complete()``, which run inside other SeleniumBase methods to ensure that it's safe to proceed with the next command.
+* **(2)**: SeleniumBase includes methods such as `wait_for_ready_state_complete()`, which run inside other SeleniumBase methods to ensure that it's safe to proceed with the next command.
* **(3)**: SeleniumBase methods automatically wait for elements to be visible and interactable before interacting with those elements.
**If you want to speed up your tests and you think the third level of protection is enough by itself, you can use command-line options to remove the first, the second, or both of those first two levels of protection:**
-* ``--pls=none`` --> Set ``pageLoadStrategy`` to ``"none"``: This strategy causes Selenium to return immediately after the initial HTML content is fully received by the browser.
+* `--pls=none` --> Set `pageLoadStrategy` to "none": This strategy causes Selenium to return immediately after the initial HTML content is fully received by the browser.
-* ``--sjw`` --> Skip JS Waits, such as ``wait_for_ready_state_complete()``.
+* `--sjw` --> Skip JS Waits, such as `wait_for_ready_state_complete()`.
--------
diff --git a/help_docs/js_package_manager.md b/help_docs/js_package_manager.md
index e7c942a453b..26390f356ec 100644
--- a/help_docs/js_package_manager.md
+++ b/help_docs/js_package_manager.md
@@ -8,7 +8,7 @@
🎦 (Demo Mode)
-🚎 (Website Tours)
+🚎 (Tour Maker)
🎞️ (Presentation Maker)
diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md
index 77a70edf85d..fc3894bda9f 100644
--- a/help_docs/method_summary.md
+++ b/help_docs/method_summary.md
@@ -90,14 +90,16 @@ self.find_elements(selector, by="css selector", limit=0)
# Duplicates:
# self.select_all(selector, by="css selector", limit=0)
self.find_visible_elements(selector, by="css selector", limit=0)
-self.click_visible_elements(selector, by="css selector", limit=0, timeout=None)
-self.click_nth_visible_element(selector, number, by="css selector", timeout=None)
-self.click_if_visible(selector, by="css selector", timeout=0)
+self.click_visible_elements(
+ selector, by="css selector", limit=0, timeout=None, scroll=True)
+self.click_nth_visible_element(
+ selector, number, by="css selector", timeout=None, scroll=True)
+self.click_if_visible(selector, by="css selector", timeout=0, scroll=True)
self.click_active_element()
self.click_with_offset(
- selector, x, y, by="css selector", mark=None, timeout=None, center=None)
+ selector, x, y, by="css selector", mark=None, timeout=None, center=None, scroll=True)
self.double_click_with_offset(
- selector, x, y, by="css selector", mark=None, timeout=None, center=None)
+ selector, x, y, by="css selector", mark=None, timeout=None, center=None, scroll=True)
self.is_checked(selector, by="css selector", timeout=None)
# Duplicates:
# self.is_selected(selector, by="css selector", timeout=None)
diff --git a/mkdocs.yml b/mkdocs.yml
index 2a3810420ab..77e7b27d757 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -92,11 +92,13 @@ plugins:
nav:
- ✅ SeleniumBase README: README.md
- 🏰 List of Features: help_docs/features_list.md
+ - 🐙 CDP Mode: examples/cdp_mode/ReadMe.md
+ - 🎭 Stealthy Playwright: examples/cdp_mode/playwright/ReadMe.md
- 📚 Running Example Tests: examples/ReadMe.md
- 🎛️ Command Line Options: help_docs/customizing_test_runs.md
- 🪄 Console Scripts: seleniumbase/console_scripts/ReadMe.md
- 📊 Dashboard / Reports: examples/example_logs/ReadMe.md
- - 🔡 Syntax Formats: help_docs/syntax_formats.md
+ - 🔠 Syntax Formats: help_docs/syntax_formats.md
- 🎖️ GUI / Commander: help_docs/commander.md
- 🔴 Recorder Mode: help_docs/recorder_mode.md
- 📘 API Reference: help_docs/method_summary.md
@@ -115,20 +117,19 @@ nav:
- 📶 Chart Maker: examples/chart_maker/ReadMe.md
- 🎞️ Presentation Maker: examples/presenter/ReadMe.md
- Integrations:
+ - 🐳 Docker: integrations/docker/ReadMe.md
- 👤 UC Mode: help_docs/uc_mode.md
- - 🐙 CDP Mode: examples/cdp_mode/ReadMe.md
- - 🎭 Stealthy Playwright: examples/cdp_mode/playwright/ReadMe.md
- 🤖 GitHub CI: integrations/github/workflows/ReadMe.md
- 🛂 MasterQA: seleniumbase/masterqa/ReadMe.md
- 🗂️ Case Plans: help_docs/case_plans.md
- 📱 Mobile Mode: help_docs/mobile_testing.md
- 🌐 Selenium Grid: seleniumbase/utilities/selenium_grid/ReadMe.md
- 🖼️ Visual Testing: examples/visual_testing/ReadMe.md
- - 🕵️ The HTML Inspector: help_docs/html_inspector.md
- 🤖 Azure Pipelines: integrations/azure/azure_pipelines/ReadMe.md
- 🤖 Jenkins on Azure: integrations/azure/jenkins/ReadMe.md
- 🤖 Jenkins on Google Cloud: integrations/google_cloud/ReadMe.md
- - 🤖 NodeJS Test Runner: https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js
+ - 🕵️ The HTML Inspector: help_docs/html_inspector.md
+ - 🤖 NodeJS Test Runners: https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js
- Presentations:
- ✅ Core Presentation: https://seleniumbase.io/other/core_presentation.html
- 🎞️ Presenter Demo: https://seleniumbase.io/other/presenter.html
@@ -169,7 +170,6 @@ nav:
- 🔐 Decorators / Security: seleniumbase/common/ReadMe.md
- 🗂️ Case Plans (examples): examples/case_summary.md
- 🧭 Using Safari Driver: help_docs/using_safari_driver.md
- - 🐳 Docker Start Guide: integrations/docker/ReadMe.md
- 👤 Shadow DOM Support: help_docs/shadow_dom.md
- 👥 macOS Hidden Files: help_docs/hidden_files_info.md
- 🗄️ MySQL Instructions: help_docs/mysql_installation.md
diff --git a/mkdocs_build/prepare.py b/mkdocs_build/prepare.py
index 81d33b1d4a5..9600d50f192 100644
--- a/mkdocs_build/prepare.py
+++ b/mkdocs_build/prepare.py
@@ -92,20 +92,16 @@ def main(*args, **kwargs):
files_to_process.append(os.path.join(scanned_dir, dir_))
video_embed = (
- '
'
- '
"
+ 'origin=https://seleniumbase.io&autoplay=0&'
+ 'cc_load_policy=0&cc_lang_pref=&iv_load_policy=1&'
+ 'loop=0&modestbranding=1&rel=0&fs=1&'
+ 'playsinline=0&autohide=2&theme=dark&color=red&'
+ 'controls=1&" class="__youtube_prefs__ no-lazyload" '
+ 'title="YouTube player" allow="autoplay; encrypted-media" '
+ 'allowfullscreen="" data-no-lazy="1">'
)
updated_files_to_process = []
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt
index c15bb5df5b2..c3076d8cb10 100644
--- a/mkdocs_build/requirements.txt
+++ b/mkdocs_build/requirements.txt
@@ -3,7 +3,7 @@
regex>=2026.2.28
pymdown-extensions>=10.21
-pipdeptree>=2.33.0
+pipdeptree>=2.34.0
python-dateutil>=2.8.2
Markdown==3.10.2
click==8.3.1
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index dc6ed23571e..9dec5ae7a68 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.47.5"
+__version__ = "4.47.6"
diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py
index d74a03bcd6f..7722c0c6552 100644
--- a/seleniumbase/core/sb_cdp.py
+++ b/seleniumbase/core/sb_cdp.py
@@ -429,7 +429,7 @@ def find_visible_elements(self, selector, timeout=None):
visible_elements.append(element)
return visible_elements
- def click_nth_element(self, selector, number):
+ def click_nth_element(self, selector, number, scroll=True):
elements = self.select_all(selector)
if len(elements) < number:
raise Exception(
@@ -440,10 +440,11 @@ def click_nth_element(self, selector, number):
if number < 0:
number = 0
element = elements[number]
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
element.click()
- def click_nth_visible_element(self, selector, number):
+ def click_nth_visible_element(self, selector, number, scroll=True):
"""Finds all matching page elements and clicks the nth visible one.
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
(Clicks the 5th visible checkbox on the page.)"""
@@ -457,7 +458,8 @@ def click_nth_visible_element(self, selector, number):
if number < 0:
number = 0
element = elements[number]
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
element.click()
def click_link(self, link_text):
@@ -793,12 +795,13 @@ def get_active_element_css(self):
js_code = js_code.replace("return getBestSelector", "getBestSelector")
return self.loop.run_until_complete(self.page.evaluate(js_code))
- def click(self, selector, timeout=None):
+ def click(self, selector, timeout=None, scroll=True):
if not timeout:
timeout = settings.SMALL_TIMEOUT
self.__slow_mode_pause_if_set()
element = self.find_element(selector, timeout=timeout)
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
tag_name = element.tag_name
if tag_name:
tag_name = tag_name.lower().strip()
@@ -824,10 +827,10 @@ def click_active_element(self):
self.__slow_mode_pause_if_set()
self.loop.run_until_complete(self.page.wait(0.2))
- def click_if_visible(self, selector, timeout=0):
+ def click_if_visible(self, selector, timeout=0, scroll=True):
if self.is_element_visible(selector):
with suppress(Exception):
- self.click(selector, timeout=1)
+ self.click(selector, timeout=1, scroll=scroll)
elif timeout == 0:
return
else:
@@ -836,7 +839,7 @@ def click_if_visible(self, selector, timeout=0):
if self.is_element_visible(selector):
self.click(selector, timeout=1)
- def click_visible_elements(self, selector, limit=0):
+ def click_visible_elements(self, selector, limit=0, scroll=True):
"""Finds all matching page elements and clicks visible ones in order.
If a click reloads or opens a new page, the clicking will stop.
If no matching elements appear, an Exception will be raised.
@@ -859,7 +862,8 @@ def click_visible_elements(self, selector, limit=0):
except Exception:
continue
if (width != 0 or height != 0):
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
element.click()
click_count += 1
time.sleep(0.044)
@@ -868,13 +872,14 @@ def click_visible_elements(self, selector, limit=0):
except Exception:
break
- def mouse_click(self, selector, timeout=None):
+ def mouse_click(self, selector, timeout=None, scroll=True):
"""(Attempt simulating a mouse click)"""
if not timeout:
timeout = settings.SMALL_TIMEOUT
self.__slow_mode_pause_if_set()
element = self.find_element(selector, timeout=timeout)
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
element.mouse_click()
self.__slow_mode_pause_if_set()
self.loop.run_until_complete(self.page.wait(0.2))
@@ -1970,9 +1975,10 @@ def gui_click_with_offset(
py = element_rect["y"]
self.gui_click_x_y(px + x, py + y, timeframe=timeframe)
- def click_with_offset(self, selector, x, y, center=False):
+ def click_with_offset(self, selector, x, y, center=False, scroll=True):
element = self.find_element(selector)
- element.scroll_into_view()
+ if scroll:
+ element.scroll_into_view()
if "--debug" in sys.argv:
displayed_selector = "`%s`" % selector
if '"' not in selector:
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 2a563b57881..ea2fab9129b 100644
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -411,7 +411,7 @@ def click(
original_by = by
selector, by = self.__recalculate_selector(selector, by)
if self.__is_cdp_swap_needed():
- self.cdp.click(selector, timeout=timeout)
+ self.cdp.click(selector, timeout=timeout, scroll=scroll)
return
if delay and (type(delay) in [int, float]) and delay > 0:
time.sleep(delay)
@@ -2254,7 +2254,7 @@ def find_visible_elements(self, selector, by="css selector", limit=0):
)
def click_visible_elements(
- self, selector, by="css selector", limit=0, timeout=None
+ self, selector, by="css selector", limit=0, timeout=None, scroll=True
):
"""Finds all matching page elements and clicks visible ones in order.
If a click reloads or opens a new page, the clicking will stop.
@@ -2270,7 +2270,7 @@ def click_visible_elements(
timeout = self.__get_new_timeout(timeout)
selector, by = self.__recalculate_selector(selector, by)
if self.__is_cdp_swap_needed():
- self.cdp.click_visible_elements(selector, limit)
+ self.cdp.click_visible_elements(selector, limit, scroll=scroll)
return
self.wait_for_ready_state_complete()
if self.__needs_minimum_wait():
@@ -2297,7 +2297,8 @@ def click_visible_elements(
return
try:
if element.is_displayed():
- self.__scroll_to_element(element)
+ if scroll:
+ self.__scroll_to_element(element)
if self.browser == "safari":
self.execute_script("arguments[0].click();", element)
else:
@@ -2311,7 +2312,8 @@ def click_visible_elements(
time.sleep(0.12)
try:
if element.is_displayed():
- self.__scroll_to_element(element)
+ if scroll:
+ self.__scroll_to_element(element)
if self.browser == "safari":
self.execute_script(
"arguments[0].click();", element
@@ -2348,7 +2350,7 @@ def click_visible_elements(
self.__switch_to_newest_window_if_not_blank()
def click_nth_visible_element(
- self, selector, number, by="css selector", timeout=None
+ self, selector, number, by="css selector", timeout=None, scroll=True
):
"""Finds all matching page elements and clicks the nth visible one.
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
@@ -2360,7 +2362,7 @@ def click_nth_visible_element(
timeout = self.__get_new_timeout(timeout)
selector, by = self.__recalculate_selector(selector, by)
if self.__is_cdp_swap_needed():
- self.cdp.click_nth_visible_element(selector, number)
+ self.cdp.click_nth_visible_element(selector, number, scroll=scroll)
return
self.wait_for_ready_state_complete()
self.wait_for_element_present(selector, by=by, timeout=timeout)
@@ -2379,7 +2381,8 @@ def click_nth_visible_element(
pre_action_url = self.driver.current_url
pre_window_count = len(self.driver.window_handles)
try:
- self.__scroll_to_element(element)
+ if scroll:
+ self.__scroll_to_element(element)
self.__element_click(element)
except (Stale_Exception, ENI_Exception, ECI_Exception):
time.sleep(0.12)
@@ -2409,18 +2412,20 @@ def click_nth_visible_element(
):
self.__switch_to_newest_window_if_not_blank()
- def click_if_visible(self, selector, by="css selector", timeout=0):
+ def click_if_visible(
+ self, selector, by="css selector", timeout=0, scroll=True
+ ):
"""If the page selector exists and is visible, clicks on the element.
This method only clicks on the first matching element found.
Use click_visible_elements() to click all matching elements.
If a "timeout" is provided, waits that long for the element
to appear before giving up and returning without a click()."""
if self.__is_cdp_swap_needed():
- self.cdp.click_if_visible(selector, timeout=timeout)
+ self.cdp.click_if_visible(selector, timeout=timeout, scroll=scroll)
return
self.wait_for_ready_state_complete()
if self.is_element_visible(selector, by=by):
- self.click(selector, by=by)
+ self.click(selector, by=by, scroll=scroll)
elif timeout > 0:
with suppress(Exception):
self.wait_for_element_visible(
@@ -2428,7 +2433,7 @@ def click_if_visible(self, selector, by="css selector", timeout=0):
)
self.sleep(0.2)
if self.is_element_visible(selector, by=by):
- self.click(selector, by=by)
+ self.click(selector, by=by, scroll=scroll)
def click_active_element(self):
if self.__is_cdp_swap_needed():
@@ -2484,6 +2489,7 @@ def click_with_offset(
mark=None,
timeout=None,
center=None,
+ scroll=True,
):
"""Click an element at an {X,Y}-offset location.
{0,0} is the top-left corner of the element.
@@ -2500,6 +2506,7 @@ def click_with_offset(
mark=mark,
timeout=timeout,
center=center,
+ scroll=scroll,
)
def double_click_with_offset(
@@ -2511,6 +2518,7 @@ def double_click_with_offset(
mark=None,
timeout=None,
center=None,
+ scroll=True,
):
"""Double click an element at an {X,Y}-offset location.
{0,0} is the top-left corner of the element.
@@ -2527,6 +2535,7 @@ def double_click_with_offset(
mark=mark,
timeout=timeout,
center=center,
+ scroll=scroll,
)
def is_checked(self, selector, by="css selector", timeout=None):
@@ -14043,9 +14052,12 @@ def __click_with_offset(
mark=None,
timeout=None,
center=None,
+ scroll=True,
):
if self.__is_cdp_swap_needed():
- self.cdp.click_with_offset(selector, x, y, center=center)
+ self.cdp.click_with_offset(
+ selector, x, y, center=center, scroll=scroll
+ )
return
self.wait_for_ready_state_complete()
if self.__needs_minimum_wait():
@@ -14061,9 +14073,13 @@ def __click_with_offset(
if self.demo_mode:
self.__highlight(selector, by=by, loops=1)
elif self.slow_mode:
- self.__slow_scroll_to_element(element)
+ if scroll:
+ self.__slow_scroll_to_element(element)
+ else:
+ self.sleep(0.2)
else:
- self.__scroll_to_element(element, selector, by)
+ if scroll:
+ self.__scroll_to_element(element, selector, by)
self.wait_for_ready_state_complete()
if self.__needs_minimum_wait():
time.sleep(0.03)
diff --git a/seleniumbase/undetected/cdp_driver/tab.py b/seleniumbase/undetected/cdp_driver/tab.py
index 28b577cace9..0adfbc0d5c8 100644
--- a/seleniumbase/undetected/cdp_driver/tab.py
+++ b/seleniumbase/undetected/cdp_driver/tab.py
@@ -1629,11 +1629,17 @@ async def click_if_visible(self, selector, timeout=0):
await element.click_async()
async def click_with_offset(self, selector, x, y, center=False, timeout=5):
+ """Click an element at an {X,Y}-offset location.
+ {0,0} is the top-left corner of the element.
+ This method is used to click on CAPTCHAs."""
element = await self.find(selector, timeout=timeout)
await element.scroll_into_view_async()
await element.mouse_click_with_offset_async(x=x, y=y, center=center)
async def solve_captcha(self):
+ """This method does a few things to click a CAPTCHA:
+ 1. Checks to see if a CAPTCHA is on the current page.
+ 2. If found, calls `click_with_offset(*)` to click it."""
await self.sleep(0.11)
source = await self.get_html()
if await self.__on_a_cf_turnstile_page(source):