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: 1 addition & 1 deletion examples/boilerplates/samples/google_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class HomePage(object):
dialog_box = '[role="dialog"] div'
search_box = '[title="Search"]'
search_box = '[name="q"]'
search_button = 'input[value="Google Search"]'


Expand Down
4 changes: 2 additions & 2 deletions examples/test_hack_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ def test_hack_search(self):
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.hide_elements("iframe")
self.assert_element('[title="Search"]')
self.assert_element('[name="q"]')
self.sleep(0.5)
self.set_attribute('[action="/search"]', "action", "//bing.com/search")
self.set_attributes('[value="Google Search"]', "value", "Bing Search")
self.type('[title="Search"]', "SeleniumBase GitHub Page URL")
self.type('[name="q"]', "SeleniumBase GitHub Page URL")
self.sleep(0.5)
self.js_click('[value="Bing Search"]')
self.highlight("h1.b_logo", loops=8)
Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,27 +108,27 @@ class MyTourClass(BaseCase):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

# Create a website tour using the ShepherdJS library with "dark" theme
# Same as: self.create_shepherd_tour(theme="dark")
self.create_tour(theme="dark")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

# Create a website tour using the ShepherdJS library with "light" theme
# Same as: self.create_shepherd_tour(theme="light")
self.create_tour(theme="light")
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub\n")
self.ad_block()
self.wait_for_element("#search")

Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/bootstrap_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ def test_google_tour(self):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

self.create_bootstrap_tour() # OR self.create_tour(theme="bootstrap")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

self.create_bootstrap_tour()
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub\n")
self.ad_block()
self.wait_for_element("#search")

Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ def test_google_tour(self):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

# Create a website tour using the ShepherdJS library with "dark" theme
# Same as: self.create_shepherd_tour(theme="dark")
self.create_tour(theme="dark")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

# Create a website tour using the ShepherdJS library with "light" theme
# Same as: self.create_shepherd_tour(theme="light")
self.create_tour(theme="light")
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub")
self.ad_block()
self.wait_for_element("#search")

Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/hopscotch_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ def test_google_tour(self):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

self.create_hopscotch_tour() # OR self.create_tour(theme="hopscotch")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

self.create_hopscotch_tour()
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub\n")
self.ad_block()
self.wait_for_element("#search")

Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/introjs_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ def test_google_tour(self):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

self.create_introjs_tour() # OR self.create_tour(theme="introjs")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

self.create_introjs_tour()
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub\n")
self.ad_block()
self.wait_for_element("#search")

Expand Down
10 changes: 5 additions & 5 deletions examples/tour_examples/shepherd_google_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ def test_google_tour(self):
self.get_new_driver(undetectable=True)
self.goto("https://google.com/ncr")
self.click_if_visible('button:contains("Accept all")')
self.wait_for_element('[title="Search"]')
self.wait_for_element('[name="q"]')
self.hide_elements("iframe")

self.create_shepherd_tour(theme="dark")
self.add_tour_step("Welcome to Google!", title="SeleniumBase Tours")
self.add_tour_step("Type in your query here.", '[title="Search"]')
self.add_tour_step("Type in your query here.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "Google")
self.highlight_type('[name="q"]', "Google")
self.wait_for_element('[role="listbox"]') # Wait for autocomplete

self.create_shepherd_tour(theme="light")
self.add_tour_step("Then click to search.", '[value="Google Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[title="Search"]')
self.add_tour_step("Or press [ENTER] after entry.", '[name="q"]')
self.play_tour()

self.highlight_type('[title="Search"]', "GitHub\n")
self.highlight_type('[name="q"]', "GitHub\n")
self.ad_block()
self.wait_for_element("#search")

Expand Down
14 changes: 12 additions & 2 deletions examples/wordle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,23 @@ def test_wordle(self):
self.goto_if_not_url("about:blank")
self.skip("Skip this test in headless mode!")
self.goto("https://www.nytimes.com/games/wordle/index.html")
self.click_if_visible("button.purr-blocker-card__button", timeout=2)
self.sleep(0.5)
self.click_if_visible("button.purr-blocker-card__button", timeout=1)
self.sleep(0.2)
self.click_if_visible('button:contains("Play")', timeout=2)
self.sleep(0.2)
self.click_if_visible('button[class*="Skip-module"]', timeout=4)
self.sleep(0.2)
self.click_if_visible('svg[data-testid="icon-close"]', timeout=2)
self.sleep(0.2)
self.remove_elements('div[class*="Ad-module]')
self.initialize_word_list()
random.seed()
word = random.choice(self.word_list)
num_attempts = 0
found_word = False
self.wait_for_element('button[data-key]')
self.sleep(0.7)
for attempt in range(6):
num_attempts += 1
if len(self.word_list) == 0:
Expand All @@ -74,8 +81,10 @@ def test_wordle(self):
letters.append(letter)
button = 'button[data-key="%s"]' % letter
self.click(button)
self.sleep(0.2)
button = 'button[class*="oneAndAHalf"]'
self.click(button)
self.sleep(0.2)
row = (
'div[class*="Board"] div[class*="Row-module"]:nth-of-type(%s) '
% num_attempts
Expand All @@ -92,6 +101,7 @@ def test_wordle(self):
break
self.word_list.remove(word)
self.modify_word_list(word, letter_status)
self.sleep(0.2)

self.save_screenshot_to_logs()
if found_word:
Expand All @@ -104,4 +114,4 @@ def test_wordle(self):

if __name__ == "__main__":
from pytest import main
main([__file__, "-s"])
main([__file__, "-s", "--ad-block"])
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.3.7dev0"
version = "1.3.8dev0"
description = "MCP server exposing SeleniumBase CDP Mode as tools for MCP clients."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
38 changes: 20 additions & 18 deletions mcp_servers/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,15 +646,15 @@ def get_attributes(
attribute: str | None = None,
timeout: float = 5,
) -> str | dict[str, Any] | None:
"""Get a specific HTML attribute (or all attributes) from the
first-matching element. Examples of possible attributes include
href, src, value, class, id, name, type, aria-label, etc.
"""Get a specific HTML attribute (or all attributes) from the selected
element. Examples of possible attributes include href, src, value, class,
id, name, type, aria-label, etc.

Args:
selector: CSS selector or SeleniumBase-supported XPath selector.

attribute: Specific HTML attribute to retrieve. When omitted, return
all HTML attributes of the first matching element as a dictionary.
attribute: Specific HTML attribute to retrieve. When omitted, returns
all HTML attributes of the matching element as a dictionary.

timeout: Maximum seconds to wait for the target element. Default: 5.

Expand Down Expand Up @@ -1807,17 +1807,17 @@ def manage_tabs(
)
@handle_sb_errors
def solve_captcha() -> str:
"""Attempt a SeleniumBase CDP-based CAPTCHA interaction, such as clicking
"""Perform a SeleniumBase CDP-based CAPTCHA interaction, such as clicking
a CAPTCHA checkbox, or performing a drag/drop action on a slider CAPTCHA.

Supported CAPTCHAs include: Cloudflare Turnstile, reCAPTCHA, hCaptcha,
DataDome Slider, and FriendlyCaptcha.

This tool automatically detects the coordinates of CAPTCHA checkboxes
for determining the correct location to perform the click. If no CAPTCHA
is detected on the current page, then no click action is attempted.
This tool automatically detects the location of CAPTCHA checkboxes
for the click action. If none of the supported CAPTCHAs are detected
on the current page, then no click action is attempted.

The tool does not guarantee that the CAPTCHA was solved. Some CAPTCHA
The tool does not guarantee that the CAPTCHA gets solved. Some CAPTCHA
controls are embedded inside shadow DOM or otherwise do not expose an
easy success signal. A successful attempt may result in changes to page
state or browser cookies.
Expand All @@ -1826,17 +1826,19 @@ def solve_captcha() -> str:
1. Inspect the webpage with get_content when you need to
determine whether CAPTCHA-related controls are present.
2. Call 'solve_captcha' to attempt the CAPTCHA interaction.
3. Use 'get_page_info', 'get_content', 'check_if_condition',
or 'manage_cookies' to inspect resulting page/session state.
3. Use 'get_page_info', 'get_content', 'check_if_condition', or
'manage_cookies' to inspect the resulting page/session state
to determine whether the interaction appears to have succeeded.

Returns:
A confirmation message of the CAPTCHA interaction.
The message is the same for both successful and failed attempts.
A message indicating that the CAPTCHA interaction was attempted.

Notes:
Clicking with the Chrome DevTools Protocol (CDP) is generally
stealthier than clicking with JavaScript because CDP actions
can avoid triggering `isTrusted: false`.
Tool selection:
- Need to interact with one of the supported CAPTCHAs ->
use solve_captcha.
- Need to hover an element in order to click another ->
use hover_action with action="hover_and_click".
- Need to click a specific visible element -> use click_element.
"""
sb = _get_sb()
sb.solve_captcha()
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ filelock>=3.32.7
fasteners>=0.20
mycdp>=1.4.0
pynose>=1.5.5
platformdirs>=4.11.8
platformdirs>=4.11.9
typing-extensions>=4.16.0
sbvirtualdisplay>=1.4.0
MarkupSafe>=3.0.3
Expand All @@ -25,7 +25,7 @@ pyyaml>=6.0.3
pygments>=2.20.1
pyreadline3>=3.5.4;platform_system=="Windows"
tabcompleter>=1.4.1
pdbp>=1.8.2
pdbp>=1.8.3
idna>=3.19
charset-normalizer>=3.5.1,<4
urllib3>=2.8.0,<3
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.54.7"
__version__ = "4.54.8"
Loading