diff --git a/README.md b/README.md
index 3180e9f34bb..8c398897352 100755
--- a/README.md
+++ b/README.md
@@ -11,7 +11,6 @@

-
All-in-one Browser Automation Framework:
Web Crawling / Testing / Scraping / Stealth

@@ -54,7 +53,9 @@
-SeleniumBase is the professional toolkit for web automation activities. Built for testing websites, bypassing CAPTCHAs, enhancing productivity, completing tasks, and scaling your business.
+
+
+SeleniumBase is the professional toolkit for web automation. Built for testing websites, bypassing CAPTCHAs, completing tasks, and scaling your business.
--------
@@ -81,7 +82,7 @@ with SB(test=True, uc=True) as sb:
> `python raw_google.py`
-
+
--------
@@ -1393,5 +1394,5 @@ pytest --reruns=1 --reruns-delay=1
-
+
diff --git a/examples/cdp_mode/ReadMe.md b/examples/cdp_mode/ReadMe.md
index ede98774998..fb339ee2a49 100644
--- a/examples/cdp_mode/ReadMe.md
+++ b/examples/cdp_mode/ReadMe.md
@@ -413,6 +413,7 @@ sb.cdp.send_keys(selector, text, timeout=None)
sb.cdp.press_keys(selector, text, timeout=None)
sb.cdp.type(selector, text, timeout=None)
sb.cdp.set_value(selector, text, timeout=None)
+sb.cdp.submit(selector)
sb.cdp.evaluate(expression)
sb.cdp.js_dumps(obj_name)
sb.cdp.maximize()
@@ -438,6 +439,8 @@ sb.cdp.get_page_source()
sb.cdp.get_user_agent()
sb.cdp.get_cookie_string()
sb.cdp.get_locale_code()
+sb.cdp.get_local_storage_item(key)
+sb.cdp.get_session_storage_item(key)
sb.cdp.get_screen_rect()
sb.cdp.get_window_rect()
sb.cdp.get_window_size()
@@ -454,6 +457,8 @@ sb.cdp.get_element_attribute(selector, attribute)
sb.cdp.get_attribute(selector, attribute)
sb.cdp.get_element_html(selector)
sb.cdp.set_locale(locale)
+sb.cdp.set_local_storage_item(key, value)
+sb.cdp.set_session_storage_item(key, value)
sb.cdp.set_attributes(selector, attribute, value)
sb.cdp.gui_press_key(key)
sb.cdp.gui_press_keys(keys)
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt
index 19dab0dc4cb..19c65c37eae 100644
--- a/mkdocs_build/requirements.txt
+++ b/mkdocs_build/requirements.txt
@@ -3,7 +3,7 @@
regex>=2024.11.6
pymdown-extensions>=10.14.3
-pipdeptree>=2.25.0
+pipdeptree>=2.25.1
python-dateutil>=2.8.2
Markdown==3.7
click==8.1.8
diff --git a/requirements.txt b/requirements.txt
index f21539031cb..b9620764b01 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
pip>=25.0.1
packaging>=24.2
setuptools~=70.2;python_version<"3.10"
-setuptools>=75.8.2;python_version>="3.10"
+setuptools>=76.0.0;python_version>="3.10"
wheel>=0.45.1
attrs>=25.1.0
certifi>=2025.1.31
@@ -11,7 +11,7 @@ websockets>=15.0.1;python_version>="3.9"
filelock~=3.16.1;python_version<"3.9"
filelock>=3.17.0;python_version>="3.9"
fasteners>=0.19
-mycdp>=1.1.0
+mycdp>=1.1.1
pynose>=1.5.4
platformdirs>=4.3.6
typing-extensions>=4.12.2
@@ -44,7 +44,8 @@ wsproto==1.2.0
websocket-client==1.8.0
selenium==4.27.1;python_version<"3.9"
selenium==4.29.0;python_version>="3.9"
-cssselect==1.2.0
+cssselect==1.2.0;python_version<"3.9"
+cssselect==1.3.0;python_version>="3.9"
sortedcontainers==2.4.0
execnet==2.1.1
iniconfig==2.0.0
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index 00256da934d..0c4da959fab 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.35.6"
+__version__ = "4.35.7"
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 6341d61e0ac..5f8f8eaa73b 100644
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -662,6 +662,7 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.press_keys = CDPM.press_keys
cdp.type = CDPM.type
cdp.set_value = CDPM.set_value
+ cdp.submit = CDPM.submit
cdp.evaluate = CDPM.evaluate
cdp.js_dumps = CDPM.js_dumps
cdp.maximize = CDPM.maximize
@@ -670,6 +671,8 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.set_window_rect = CDPM.set_window_rect
cdp.reset_window_size = CDPM.reset_window_size
cdp.set_locale = CDPM.set_locale
+ cdp.set_local_storage_item = CDPM.set_local_storage_item
+ cdp.set_session_storage_item = CDPM.set_session_storage_item
cdp.set_attributes = CDPM.set_attributes
cdp.gui_press_key = CDPM.gui_press_key
cdp.gui_press_keys = CDPM.gui_press_keys
@@ -705,6 +708,8 @@ def uc_open_with_cdp_mode(driver, url=None):
cdp.get_user_agent = CDPM.get_user_agent
cdp.get_cookie_string = CDPM.get_cookie_string
cdp.get_locale_code = CDPM.get_locale_code
+ cdp.get_local_storage_item = CDPM.get_local_storage_item
+ cdp.get_session_storage_item = CDPM.get_session_storage_item
cdp.get_text = CDPM.get_text
cdp.get_title = CDPM.get_title
cdp.get_page_title = CDPM.get_title
diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py
index 31bb25b1681..ed017b17a01 100644
--- a/seleniumbase/core/sb_cdp.py
+++ b/seleniumbase/core/sb_cdp.py
@@ -955,6 +955,20 @@ def set_value(self, selector, text, timeout=None):
self.__slow_mode_pause_if_set()
self.loop.run_until_complete(self.page.sleep(0.025))
+ def submit(self, selector):
+ submit_script = (
+ """elm = document.querySelector('%s');
+ const event = new KeyboardEvent("keydown", {
+ key: "Enter",
+ keyCode: 13,
+ code: "Enter",
+ which: 13,
+ bubbles: true
+ });
+ elm.dispatchEvent(event);""" % selector
+ )
+ self.loop.run_until_complete(self.page.evaluate(submit_script))
+
def evaluate(self, expression):
"""Run a JavaScript expression and return the result."""
expression = expression.strip()
@@ -1115,6 +1129,16 @@ def get_locale_code(self):
self.page.evaluate("navigator.language || navigator.languages[0]")
)
+ def get_local_storage_item(self, key):
+ js_code = """localStorage.getItem('%s');""" % key
+ with suppress(Exception):
+ return self.loop.run_until_complete(self.page.evaluate(js_code))
+
+ def get_session_storage_item(self, key):
+ js_code = """sessionStorage.getItem('%s');""" % key
+ with suppress(Exception):
+ return self.loop.run_until_complete(self.page.evaluate(js_code))
+
def get_screen_rect(self):
coordinates = self.loop.run_until_complete(
self.page.js_dumps("window.screen")
@@ -1302,6 +1326,16 @@ def set_locale(self, locale):
"""(Settings will take effect on the next page load)"""
self.loop.run_until_complete(self.page.set_locale(locale))
+ def set_local_storage_item(self, key, value):
+ js_code = """localStorage.setItem('%s','%s');""" % (key, value)
+ with suppress(Exception):
+ self.loop.run_until_complete(self.page.evaluate(js_code))
+
+ def set_session_storage_item(self, key, value):
+ js_code = """sessionStorage.setItem('%s','%s');""" % (key, value)
+ with suppress(Exception):
+ self.loop.run_until_complete(self.page.evaluate(js_code))
+
def set_attributes(self, selector, attribute, value):
"""This method uses JavaScript to set/update a common attribute.
All matching selectors from querySelectorAll() are used.
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 488ad617ef4..dad222fd6d7 100644
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -1164,6 +1164,9 @@ def submit(self, selector, by="css selector"):
"""Alternative to self.driver.find_element_by_*(SELECTOR).submit()"""
self.__check_scope()
selector, by = self.__recalculate_selector(selector, by)
+ if self.__is_cdp_swap_needed():
+ self.cdp.submit(selector)
+ return
element = self.wait_for_element_clickable(
selector, by=by, timeout=settings.SMALL_TIMEOUT
)
@@ -8800,6 +8803,9 @@ def set_local_storage_item(self, key, value):
self.__check_scope()
if not self.__is_valid_storage_url():
raise WebDriverException("Local Storage is not available here!")
+ if self.__is_cdp_swap_needed():
+ self.cdp.set_local_storage_item(key, value)
+ return
self.execute_script(
"window.localStorage.setItem('{}', '{}');".format(key, value)
)
@@ -8808,6 +8814,8 @@ def get_local_storage_item(self, key):
self.__check_scope()
if not self.__is_valid_storage_url():
raise WebDriverException("Local Storage is not available here!")
+ if self.__is_cdp_swap_needed():
+ return self.cdp.get_local_storage_item(key)
return self.execute_script(
"return window.localStorage.getItem('{}');".format(key)
)
@@ -8859,6 +8867,9 @@ def set_session_storage_item(self, key, value):
self.__check_scope()
if not self.__is_valid_storage_url():
raise WebDriverException("Session Storage is not available here!")
+ if self.__is_cdp_swap_needed():
+ self.cdp.set_session_storage_item(key, value)
+ return
self.execute_script(
"window.sessionStorage.setItem('{}', '{}');".format(key, value)
)
@@ -8867,6 +8878,8 @@ def get_session_storage_item(self, key):
self.__check_scope()
if not self.__is_valid_storage_url():
raise WebDriverException("Session Storage is not available here!")
+ if self.__is_cdp_swap_needed():
+ return self.cdp.get_session_storage_item(key)
return self.execute_script(
"return window.sessionStorage.getItem('{}');".format(key)
)
diff --git a/setup.py b/setup.py
index 6d6a804d69b..50df36e3ca0 100755
--- a/setup.py
+++ b/setup.py
@@ -150,7 +150,7 @@
'pip>=25.0.1',
'packaging>=24.2',
'setuptools~=70.2;python_version<"3.10"', # Newer ones had issues
- 'setuptools>=75.8.2;python_version>="3.10"',
+ 'setuptools>=76.0.0;python_version>="3.10"',
'wheel>=0.45.1',
'attrs>=25.1.0',
"certifi>=2025.1.31",
@@ -160,7 +160,7 @@
'filelock~=3.16.1;python_version<"3.9"',
'filelock>=3.17.0;python_version>="3.9"',
'fasteners>=0.19',
- "mycdp>=1.1.0",
+ "mycdp>=1.1.1",
"pynose>=1.5.4",
'platformdirs>=4.3.6',
'typing-extensions>=4.12.2',
@@ -193,7 +193,8 @@
'websocket-client==1.8.0',
'selenium==4.27.1;python_version<"3.9"',
'selenium==4.29.0;python_version>="3.9"',
- 'cssselect==1.2.0',
+ 'cssselect==1.2.0;python_version<"3.9"',
+ 'cssselect==1.3.0;python_version>="3.9"',
"sortedcontainers==2.4.0",
'execnet==2.1.1',
'iniconfig==2.0.0',