diff --git a/examples/cdp_mode/playwright/raw_etsy_sync.py b/examples/cdp_mode/playwright/raw_etsy_sync.py
new file mode 100644
index 00000000000..6705ac4b31a
--- /dev/null
+++ b/examples/cdp_mode/playwright/raw_etsy_sync.py
@@ -0,0 +1,31 @@
+from playwright.sync_api import sync_playwright
+from seleniumbase import sb_cdp
+
+sb = sb_cdp.Chrome(ad_block=True)
+sb.goto("https://www.etsy.com/")
+sb.sleep(1)
+search = "FIFA Keychains"
+required_text = "keychain"
+sb.type('input[data-id="search-query"]', search)
+sb.sleep(1)
+sb.click('button[aria-label="Search"]')
+sb.sleep(2)
+sb.click_if_visible('button[aria-label="Ok"]')
+endpoint_url = sb.get_endpoint_url()
+
+with sync_playwright() as p:
+ browser = p.chromium.connect_over_cdp(endpoint_url)
+ page = browser.contexts[0].pages[0]
+ items = page.locator("div.v2-listing-card__info")
+ num = 0
+ for i in range(items.count()):
+ title = items.nth(i).locator("h3.v2-listing-card__title").inner_text()
+ price = items.nth(i).locator("div.n-listing-card__price").inner_text()
+ if required_text.lower() in title.lower():
+ num += 1
+ title = " ".join(title.split()).strip()
+ price = price.replace("Sale Price", "").strip().split("\n")[0]
+ print(f"* <====== {num} ======>")
+ print(title)
+ print(price.strip().split("\n")[0])
+ print(f"*** {num} total items found!")
diff --git a/examples/cdp_mode/raw_cdp_etsy.py b/examples/cdp_mode/raw_cdp_etsy.py
new file mode 100644
index 00000000000..0d35d9bbc3a
--- /dev/null
+++ b/examples/cdp_mode/raw_cdp_etsy.py
@@ -0,0 +1,29 @@
+from seleniumbase import sb_cdp
+
+sb = sb_cdp.Chrome(ad_block=True)
+sb.goto("https://www.etsy.com/")
+sb.sleep(1)
+search = "FIFA Keychains"
+required_text = "keychain"
+sb.type('input[data-id="search-query"]', search)
+sb.sleep(1)
+sb.click('button[aria-label="Search"]')
+sb.sleep(2)
+sb.click_if_visible('button[aria-label="Ok"]')
+soup = sb.get_beautiful_soup()
+items = soup.select("div.v2-listing-card__info")
+num = 0
+for item in items:
+ title_element = item.select_one("h3.v2-listing-card__title")
+ price_element = item.select_one("div.n-listing-card__price")
+ if title_element and price_element:
+ title = title_element.get_text()
+ price = price_element.get_text()
+ if required_text.lower() in title.lower():
+ num += 1
+ title = " ".join(title.split()).strip()
+ price = price.replace("Sale Price", "").strip().split("\n")[0]
+ print(f"* <====== {num} ======>")
+ print(title)
+ print(price.strip().split("\n")[0])
+print(f"*** {num} total items found!")
diff --git a/examples/cdp_mode/raw_cdp_nordstrom.py b/examples/cdp_mode/raw_cdp_nordstrom.py
index ab7e90562af..3a91121f197 100644
--- a/examples/cdp_mode/raw_cdp_nordstrom.py
+++ b/examples/cdp_mode/raw_cdp_nordstrom.py
@@ -8,9 +8,6 @@
search = "cocktail dresses for women teal"
sb.press_keys("input#keyword-search-input", search + "\n")
sb.sleep(2.2)
-for i in range(17):
- sb.scroll_down(16)
- sb.sleep(0.14)
print('*** Nordstrom Search for "%s":' % search)
unique_item_text = []
items = sb.find_elements("article")
@@ -23,4 +20,5 @@
if price:
price_text = price.text
print("* %s (%s)" % (description.text, price_text))
+ sb.scroll_down(16)
sb.quit()
diff --git a/examples/cdp_mode/raw_cdp_zillow.py b/examples/cdp_mode/raw_cdp_zillow.py
index 512cae5439e..a87c83106a6 100644
--- a/examples/cdp_mode/raw_cdp_zillow.py
+++ b/examples/cdp_mode/raw_cdp_zillow.py
@@ -14,11 +14,10 @@
items = sb.find_visible_elements('[data-testid="property-card"]')
print('*** %s:' % search)
for i, item in enumerate(items):
- print("<----- %s ----->" % (i + 1))
- print(item.query_selector('[data-testid*="price"]').text)
+ print(f"* <====== {i + 1} ======>")
print(item.query_selector('[data-testid*="details"]').text)
print(item.query_selector('[data-testid*="address"]').text)
+ print(item.query_selector('[data-testid*="price"]').text)
item.scroll_into_view()
-print("<-------------->")
sb.sleep(2)
sb.quit()
diff --git a/examples/cdp_mode/raw_indeed_jobs.py b/examples/cdp_mode/raw_indeed_jobs.py
new file mode 100644
index 00000000000..f56fcb09189
--- /dev/null
+++ b/examples/cdp_mode/raw_indeed_jobs.py
@@ -0,0 +1,16 @@
+from seleniumbase import SB
+
+with SB(uc=True, test=True, use_chromium=True, incognito=True) as sb:
+ sb.goto("https://www.indeed.com/?from=gnav-compui")
+ sb.sleep(0.6)
+ search = "AI Engineer"
+ sb.type('input[name="q"]', search)
+ sb.click('button[type="submit"]')
+ sb.sleep(3.6)
+ items = sb.find_visible_elements('[data-testid*="jobcard"]')
+ print('*** %s:' % search)
+ for i, item in enumerate(items):
+ print(f"* <====== {i + 1} ======>")
+ print(item.text)
+ item.scroll_into_view()
+ print(f"*** {len(items)} total items found!")
diff --git a/examples/cdp_mode/raw_nordstrom.py b/examples/cdp_mode/raw_nordstrom.py
index 354231b5073..e5e7bdce6cc 100644
--- a/examples/cdp_mode/raw_nordstrom.py
+++ b/examples/cdp_mode/raw_nordstrom.py
@@ -9,9 +9,6 @@
search = "cocktail dresses for women teal"
sb.press_keys("input#keyword-search-input", search + "\n")
sb.sleep(2.2)
- for i in range(17):
- sb.scroll_down(16)
- sb.sleep(0.14)
print('*** Nordstrom Search for "%s":' % search)
unique_item_text = []
items = sb.find_elements("article")
@@ -24,3 +21,4 @@
if price:
price_text = price.text
print("* %s (%s)" % (description.text, price_text))
+ sb.scroll_down(16)
diff --git a/examples/cdp_mode/raw_ralphlauren.py b/examples/cdp_mode/raw_ralphlauren.py
index cdc791f970d..913b897d9b2 100644
--- a/examples/cdp_mode/raw_ralphlauren.py
+++ b/examples/cdp_mode/raw_ralphlauren.py
@@ -9,13 +9,17 @@
sb.sleep(2.2)
print("*** Ralph Lauren Search for Dresses:")
unique_item_text = []
- items = sb.find_elements('div.product-data')
+ soup = sb.get_beautiful_soup()
+ items = soup.select('div.product-data')
for item in items:
- description = item.query_selector("a.name-link")
- if description and description.text not in unique_item_text:
- unique_item_text.append(description.text)
- print("* " + description.text)
- price = item.query_selector('span[title="Price"]')
- if price:
- print(" (" + price.text.replace(" ", " ") + ")")
- item.scroll_into_view()
+ description_element = item.select_one("a.name-link")
+ if description_element:
+ description_text = description_element.get_text(strip=True)
+ if description_text not in unique_item_text:
+ unique_item_text.append(description_text)
+ print(f"* {description_text}")
+ price_element = item.select_one('span[title="Price"]')
+ if price_element:
+ price_text = " ".join(price_element.get_text().split())
+ print(f" ({price_text})")
+ sb.scroll_down(20)
diff --git a/requirements.txt b/requirements.txt
index 300a2c65c66..f4ddd7bdac6 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@ pip>=26.0.1;python_version<"3.10"
pip>=26.1.2;python_version>="3.10"
packaging>=26.2
setuptools~=70.2;python_version<"3.10"
-setuptools>=82.0.1;python_version>="3.10"
+setuptools>=83.0.0;python_version>="3.10"
wheel>=0.47.0
attrs>=26.1.0
certifi>=2026.6.17
@@ -10,7 +10,7 @@ exceptiongroup>=1.3.1
websockets~=15.0.1;python_version<"3.10"
websockets>=16.0;python_version>="3.10"
filelock~=3.19.1;python_version<"3.10"
-filelock>=3.29.5;python_version>="3.10"
+filelock>=3.29.6;python_version>="3.10"
fasteners>=0.20
mycdp>=1.4.0
pynose>=1.5.5
@@ -31,7 +31,7 @@ pyreadline3>=3.5.4;platform_system=="Windows"
tabcompleter>=1.4.1
pdbp>=1.8.2
idna>=3.18
-charset-normalizer>=3.4.7,<4
+charset-normalizer>=3.4.9,<4
urllib3>=1.26.20,<2;python_version<"3.10"
urllib3>=2.7.0,<3;python_version>="3.10"
requests~=2.32.5;python_version<"3.10"
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index 0049e708595..9159d1b7954 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.50.5"
+__version__ = "4.50.6"
diff --git a/seleniumbase/core/sb_cdp.py b/seleniumbase/core/sb_cdp.py
index 8b12c42b4f3..c6c7cc25daa 100644
--- a/seleniumbase/core/sb_cdp.py
+++ b/seleniumbase/core/sb_cdp.py
@@ -3446,33 +3446,33 @@ def js_scroll_into_view(self, selector):
def scroll_into_view(self, selector):
self.find_element(selector).scroll_into_view()
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_to_y(self, y):
y = int(y)
js_code = "window.scrollTo(0, %s);" % y
with suppress(Exception):
self.loop.run_until_complete(self.page.evaluate(js_code))
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_by_y(self, y):
y = int(y)
js_code = "window.scrollBy(0, %s);" % y
with suppress(Exception):
self.loop.run_until_complete(self.page.evaluate(js_code))
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_to_top(self):
js_code = "window.scrollTo(0, 0);"
with suppress(Exception):
self.loop.run_until_complete(self.page.evaluate(js_code))
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_to_bottom(self):
js_code = "window.scrollTo(0, 10000);"
with suppress(Exception):
self.loop.run_until_complete(self.page.evaluate(js_code))
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_up(self, amount=25):
"""Scrolls up as a percentage of the page."""
@@ -3481,7 +3481,7 @@ def scroll_up(self, amount=25):
except Exception:
amount = self.get_window_size()["height"] * amount / 100
self.execute_script("window.scrollBy(0, -%s);" % amount)
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def scroll_down(self, amount=25):
"""Scrolls down as a percentage of the page."""
@@ -3490,7 +3490,7 @@ def scroll_down(self, amount=25):
except Exception:
amount = self.get_window_size()["height"] * amount / 100
self.execute_script("window.scrollBy(0, %s);" % amount)
- self.loop.run_until_complete(self.page.wait(0.1))
+ self.loop.run_until_complete(self.page.wait(0.05))
def save_page_source(self, name, folder=None):
from seleniumbase.core import log_helper
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index cb1e258028e..c87ca8479bf 100644
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -16856,15 +16856,15 @@ def __process_dashboard(self, has_exception, init=False):
''
''
' "
"'
- 'Failed: %s '
+ 'Failed: %s '
''
- 'Skipped: %s '
+ 'Skipped: %s '
''
- 'Passed: %s '
+ 'Passed: %s '
''
- 'Untested: %s '
+ 'Untested: %s '
''
- 'Total: %s '
+ ' Total: %s'
"