Skip to content
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
2 changes: 2 additions & 0 deletions help_docs/method_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,9 @@ driver.press_keys(selector, text)
driver.type(selector, text)
driver.submit(selector)
driver.assert_element(selector)
driver.assert_element_visible(selector)
driver.assert_element_present(selector)
driver.assert_element_absent(selector)
driver.assert_element_not_visible(selector)
driver.assert_text(text, selector)
driver.assert_exact_text(text, selector)
Expand Down
2 changes: 1 addition & 1 deletion mcp_servers/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "seleniumbase-mcp"
version = "1.4.2dev0"
version = "1.5.0dev0"
description = "MCP server exposing SeleniumBase CDP Mode as tools for MCP clients."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
12 changes: 7 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ certifi>=2026.7.22
exceptiongroup>=1.3.1
websockets~=16.1.1;python_version=="3.10"
websockets>=16.1.1;python_version>="3.11"
filelock>=4.0.12
filelock>=4.1.0
fasteners>=0.20
mycdp>=1.4.0
pynose>=1.5.5
Expand All @@ -16,7 +16,7 @@ sbvirtualdisplay>=1.4.0
MarkupSafe>=3.0.4
Jinja2>=3.1.6
six>=1.17.0
parse>=1.22.2
parse>=1.22.3
parse-type>=0.6.6
colorama>=0.4.6
psutil>=7.2.2
Expand All @@ -36,8 +36,9 @@ trio>=0.34.0,<1
trio-websocket~=0.12.2
wsproto~=1.3.2
websocket-client~=1.9.2
selenium==4.50.0
cssselect>=1.5.0,<2
selenium==4.51.0
cssselect~=1.5.0;python_version<"3.11"
cssselect>=1.6.0,<2;python_version>="3.11"
sortedcontainers==2.4.0
execnet==2.1.2
iniconfig~=2.3.1
Expand All @@ -52,7 +53,8 @@ pytest-rerunfailures==16.7;python_version>="3.11"
pytest-xdist==3.8.0
parameterized==0.9.0
behave==1.2.6
soupsieve~=2.10.0
soupsieve~=2.10.0;python_version=="3.10"
soupsieve~=3.0.1;python_version>="3.11"
beautifulsoup4~=4.15.0
pyotp~=2.10.0
python-xlib==0.33;platform_system=="Linux"
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.55.2"
__version__ = "4.56.0"
49 changes: 31 additions & 18 deletions seleniumbase/core/browser_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ def extend_driver(
page.type = DM.update_text
page.submit = DM.submit
page.assert_element = DM.assert_element_visible
page.assert_element_visible = DM.assert_element_visible
page.assert_element_present = DM.assert_element_present
page.assert_element_absent = DM.assert_element_absent
page.assert_element_not_visible = DM.assert_element_not_visible
page.assert_text = DM.assert_text
page.assert_exact_text = DM.assert_exact_text
Expand Down Expand Up @@ -247,7 +249,9 @@ def extend_driver(
driver.type = DM.update_text
driver.submit = DM.submit
driver.assert_element = DM.assert_element_visible
driver.assert_element_visible = DM.assert_element_visible
driver.assert_element_present = DM.assert_element_present
driver.assert_element_absent = DM.assert_element_absent
driver.assert_element_not_visible = DM.assert_element_not_visible
driver.assert_text = DM.assert_text
driver.assert_exact_text = DM.assert_exact_text
Expand Down Expand Up @@ -3259,7 +3263,7 @@ def get_driver(
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
log_d(
"\nWarning: Chrome for Testing binary not found..."
"\nINFO: Chrome for Testing binary not found..."
)
try:
sb_install.main(override="cft")
Expand All @@ -3278,7 +3282,7 @@ def get_driver(
if not os.path.exists(binary_location):
sys_args = sys.argv # Save a copy of sys args
log_d(
"\nWarning: "
"\nINFO: "
"Chrome for Testing binary not found..."
)
sb_install.main(override="cft")
Expand Down Expand Up @@ -3313,7 +3317,7 @@ def get_driver(
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
log_d(
"\nWarning: "
"\nINFO: "
"Chrome-Headless-Shell binary not found..."
)
try:
Expand All @@ -3336,7 +3340,7 @@ def get_driver(
if not os.path.exists(binary_location):
sys_args = sys.argv # Save a copy of sys args
log_d(
"\nWarning: "
"\nINFO: "
"Chrome-Headless-Shell binary not found..."
)
sb_install.main(override="chs")
Expand Down Expand Up @@ -4211,7 +4215,7 @@ def get_local_driver(
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
log_d("\nWarning: geckodriver not found. Getting it now:")
log_d("\nINFO: geckodriver not found. Getting it now:")
try:
sb_install.main(override="geckodriver")
except Exception as e:
Expand All @@ -4229,7 +4233,7 @@ def get_local_driver(
if not geckodriver_on_path():
sys_args = sys.argv # Save a copy of sys args
log_d(
"\nWarning: geckodriver not found. "
"\nINFO: geckodriver not found. "
"Getting it now:"
)
sb_install.main(override="geckodriver")
Expand Down Expand Up @@ -4518,9 +4522,9 @@ def get_local_driver(
args = " ".join(sys.argv)
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
msg = "Microsoft Edge Driver not found."
msg = "INFO: Microsoft Edge Driver not found."
if edgedriver_upgrade_needed:
msg = "Microsoft Edge Driver update needed."
msg = "INFO: Microsoft Edge Driver update needed."
sys_args = sys.argv # Save a copy of current sys args
log_d("\n%s Getting it now:" % msg)
sb_install.main(override="edgedriver %s" % use_version)
Expand All @@ -4534,9 +4538,9 @@ def get_local_driver(
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
msg = "Microsoft Edge Driver not found."
msg = "INFO: Microsoft Edge Driver not found."
if edgedriver_upgrade_needed:
msg = "Microsoft Edge Driver update needed."
msg = "INFO: Microsoft Edge Driver update needed."
sys_args = sys.argv # Save a copy of current sys args
log_d("\n%s Getting it now:" % msg)
sb_install.main(override="edgedriver %s" % use_version)
Expand Down Expand Up @@ -5329,13 +5333,13 @@ def get_local_driver(
if not ("-n" in sys.argv or " -n=" in args or args == "-c"):
# (Not multithreaded)
sys_args = sys.argv # Save a copy of current sys args
msg = "chromedriver update needed. Getting it now:"
msg = "INFO: chromedriver update needed. Getting it now:"
if not path_chromedriver:
msg = "chromedriver not found. Getting it now:"
msg = "INFO: chromedriver not found. Getting it now:"
if use_uc and not os.path.exists(local_uc_driver):
msg = "uc_driver not found. Getting it now:"
msg = "INFO: uc_driver not found. Getting it now:"
if use_uc and os.path.exists(local_uc_driver):
msg = "uc_driver update needed. Getting it now:"
msg = "INFO: uc_driver update needed. Getting it now:"
log_d("\nWarning: %s" % msg)
force_uc = False
intel_for_uc = False
Expand Down Expand Up @@ -5380,13 +5384,22 @@ def get_local_driver(
shared_utils.make_writable(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
msg = "chromedriver update needed. Getting it now:"
msg = (
"INFO: chromedriver update needed. Getting it now:"
)
if not path_chromedriver:
msg = "chromedriver not found. Getting it now:"
msg = (
"INFO: chromedriver not found. Getting it now:"
)
if use_uc and not os.path.exists(local_uc_driver):
msg = "uc_driver not found. Getting it now:"
msg = (
"INFO: uc_driver not found. Getting it now:"
)
if use_uc and os.path.exists(local_uc_driver):
msg = "uc_driver update needed. Getting it now:"
msg = (
"INFO: uc_driver update needed. "
"Getting it now:"
)
force_uc = False
intel_for_uc = False
if use_uc:
Expand Down
3 changes: 3 additions & 0 deletions seleniumbase/core/sb_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ def assert_element_visible(self, *args, **kwargs):
def assert_element_present(self, *args, **kwargs):
page_actions.assert_element_present(self.driver, *args, **kwargs)

def assert_element_absent(self, *args, **kwargs):
page_actions.assert_element_absent(self.driver, *args, **kwargs)

def assert_element_not_visible(self, *args, **kwargs):
page_actions.assert_element_not_visible(self.driver, *args, **kwargs)

Expand Down
50 changes: 34 additions & 16 deletions seleniumbase/fixtures/page_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ def wait_for_element_absent(
elif page_utils.is_valid_by(selector):
original_selector = by
selector, by = page_utils.recalculate_selector(original_selector, by)
driver.cdp.wait_for_element_absent(selector)
driver.cdp.wait_for_element_absent(selector, timeout=timeout)
return True
_reconnect_if_disconnected(driver)
start_ms = time.time() * 1000.0
Expand Down Expand Up @@ -1173,7 +1173,7 @@ def wait_for_element_not_visible(
elif page_utils.is_valid_by(selector):
original_selector = by
selector, by = page_utils.recalculate_selector(original_selector, by)
driver.cdp.wait_for_element_not_visible(selector)
driver.cdp.wait_for_element_not_visible(selector, timeout=timeout)
return True
_reconnect_if_disconnected(driver)
start_ms = time.time() * 1000.0
Expand Down Expand Up @@ -1777,7 +1777,7 @@ def open_url(driver, url):
def click(driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.click(selector)
driver.cdp.click(selector, timeout=timeout)
return
element = wait_for_element_clickable(
driver, selector, by=by, timeout=timeout
Expand All @@ -1800,7 +1800,7 @@ def click_if_visible(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.click_if_visible(selector)
driver.cdp.click_if_visible(selector, timeout=timeout)
return
if is_element_visible(driver, selector, by=by):
click(driver, selector, by=by, timeout=1)
Expand All @@ -1825,7 +1825,7 @@ def js_click(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.click(selector)
driver.cdp.click(selector, timeout=timeout)
return
element = wait_for_element_present(
driver, selector, by=by, timeout=timeout
Expand Down Expand Up @@ -1853,7 +1853,7 @@ def send_keys(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.send_keys(selector, text)
driver.cdp.send_keys(selector, text, timeout=timeout)
return
element = wait_for_element_present(
driver, selector, by=by, timeout=timeout
Expand All @@ -1870,7 +1870,7 @@ def press_keys(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.press_keys(selector, text)
driver.cdp.press_keys(selector, text, timeout=timeout)
return
element = wait_for_element_present(
driver, selector, by=by, timeout=timeout
Expand All @@ -1889,7 +1889,7 @@ def update_text(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.type(selector, text)
driver.cdp.type(selector, text, timeout=timeout)
return
element = wait_for_element_clickable(
driver, selector, by=by, timeout=timeout
Expand Down Expand Up @@ -1932,7 +1932,7 @@ def assert_element_visible(
original_selector = by
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.assert_element(selector)
driver.cdp.assert_element(selector, timeout=timeout)
return True
wait_for_element_visible(
driver,
Expand All @@ -1953,7 +1953,7 @@ def assert_element_present(
original_selector = by
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.assert_element_present(selector)
driver.cdp.assert_element_present(selector, timeout=timeout)
return True
wait_for_element_present(
driver,
Expand All @@ -1964,6 +1964,24 @@ def assert_element_present(
)


def assert_element_absent(
driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT
):
original_selector = None
if page_utils.is_valid_by(by):
original_selector = selector
elif page_utils.is_valid_by(selector):
original_selector = by
selector, by = page_utils.recalculate_selector(selector, by)
wait_for_element_absent(
driver,
selector,
by=by,
timeout=timeout,
original_selector=original_selector,
)


def assert_element_not_visible(
driver, selector, by="css selector", timeout=settings.SMALL_TIMEOUT
):
Expand Down Expand Up @@ -1991,7 +2009,7 @@ def assert_text(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.assert_text(text, selector)
driver.cdp.assert_text(text, selector, timeout=timeout)
return True
wait_for_text_visible(
driver, text.strip(), selector, by=by, timeout=timeout
Expand All @@ -2007,7 +2025,7 @@ def assert_exact_text(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
driver.cdp.assert_exact_text(text, selector)
driver.cdp.assert_exact_text(text, selector, timeout=timeout)
return True
wait_for_exact_text_visible(
driver, text.strip(), selector, by=by, timeout=timeout
Expand Down Expand Up @@ -2052,7 +2070,7 @@ def wait_for_element(
original_selector = by
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
return driver.cdp.select(selector)
return driver.cdp.select(selector, timeout=timeout)
return wait_for_element_visible(
driver=driver,
selector=selector,
Expand All @@ -2075,7 +2093,7 @@ def wait_for_selector(
original_selector = by
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
return driver.cdp.select(selector)
return driver.cdp.select(selector, timeout=timeout)
return wait_for_element_present(
driver=driver,
selector=selector,
Expand All @@ -2094,7 +2112,7 @@ def wait_for_text(
):
selector, by = page_utils.recalculate_selector(selector, by)
if __is_cdp_swap_needed(driver):
return driver.cdp.find_element(selector)
return driver.cdp.find_element(selector, timeout=timeout)
return wait_for_text_visible(
driver=driver,
text=text,
Expand Down Expand Up @@ -2143,7 +2161,7 @@ def get_text(
timeout=settings.LARGE_TIMEOUT
):
if __is_cdp_swap_needed(driver):
return driver.cdp.get_text(selector)
return driver.cdp.get_text(selector, timeout=timeout)
element = wait_for_element(
driver=driver,
selector=selector,
Expand Down
Loading
Loading