Skip to content

Commit d3851ab

Browse files
committed
Add a new migration guide
1 parent cbeb8dc commit d3851ab

23 files changed

+1920
-0
lines changed

examples/migration/raw_selenium/to_playwright/01_hybrid_approach/ReadMe.md

Lines changed: 383 additions & 0 deletions
Large diffs are not rendered by default.

examples/migration/raw_selenium/to_playwright/01_hybrid_approach/__init__.py

Whitespace-only changes.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<!-- SeleniumBase Docs -->
2+
3+
## 🛠️ Standalone Automation Scripts (Non-Test)
4+
5+
This folder contains examples for users who need to perform web automation **without** using a test runner like `pytest` or `unittest`.
6+
7+
By using the hybrid approach seen in [raw_hybrid_script.py](./raw_hybrid_script.py), SeleniumBase has effectively bridged the gap between raw Selenium and Playwright in order to ease the migration process.
8+
9+
```python
10+
"""Hybrid Script Example"""
11+
from playwright.sync_api import sync_playwright, expect
12+
from selenium.webdriver.common.by import By
13+
from selenium.webdriver.support import expected_conditions as EC
14+
from selenium.webdriver.support.ui import WebDriverWait
15+
from seleniumbase import SB
16+
17+
18+
with SB(uc=True) as sb: # SeleniumBase API
19+
sb.activate_cdp_mode()
20+
sb.reconnect() # Reconnects WebDriver to use Selenium
21+
driver = sb.driver # Selenium API
22+
endpoint_url = sb.cdp.get_endpoint_url()
23+
with sync_playwright() as p:
24+
browser = p.chromium.connect_over_cdp(endpoint_url)
25+
page = browser.contexts[0].pages[0] # Playwright API
26+
27+
# This section uses Selenium
28+
driver.get("https://www.saucedemo.com")
29+
by_css = By.CSS_SELECTOR # "css selector"
30+
element = driver.find_element(by_css, "#user-name")
31+
element.send_keys("standard_user")
32+
element = driver.find_element(by_css, "#password")
33+
element.send_keys("secret_sauce")
34+
element.submit()
35+
driver.find_element(by_css, "div.inventory_list")
36+
element = driver.find_element(by_css, "span.title")
37+
assert element.text == "Products"
38+
39+
# This section uses Playwright
40+
page.click('button[name*="backpack"]')
41+
page.click("#shopping_cart_container a")
42+
expect(page.locator("span.title")).to_have_text("Your Cart")
43+
expect(page.locator("div.cart_item")).to_contain_text("Backpack")
44+
45+
# This section uses SeleniumBase
46+
sb.click("#remove-sauce-labs-backpack")
47+
sb.assert_element_not_visible("div.cart_item")
48+
49+
# This section uses Selenium
50+
driver.find_element(by_css, "#react-burger-menu-btn").click()
51+
element = WebDriverWait(driver, 10).until(
52+
EC.element_to_be_clickable((by_css, "a#logout_sidebar_link"))
53+
)
54+
element.click()
55+
driver.find_element(by_css, "input#login-button")
56+
```
57+
58+
This folder also includes standalone scripts for raw Selenium, Playwright, and SeleniumBase, which serve as foundational components for the hybrid script that integrates all three.
59+
60+
### 💡 Why use Standalone Scripts?
61+
While test runners are excellent for verification and reporting, standalone scripts are often preferred for:
62+
* **Web Scraping:** Extracting data from sites without the overhead of a test framework.
63+
* **DevOps/Utility Tasks:** Automating repetitive browser-based tasks or health checks.
64+
* **Rapid Prototyping:** Testing out a new automation flow quickly before formalizing it into a test suite.
65+
66+
### 🌉 The "SB" Advantage
67+
The `raw_hybrid_script.py` example demonstrates how to use the **`SB()` context manager**. This is a powerful feature of SeleniumBase that allows you to:
68+
1. **Bridge Engines:** Effortlessly connect Playwright to a browser session already managed by SeleniumBase.
69+
2. **Stealth by Default:** Use `uc=True` (Undetected Mode) to bypass bot-detection systems that standard Playwright configurations often fail.
70+
3. **Automatic Cleanup:** The context manager handles closing the browser and cleaning up processes automatically, even if the script encounters an error.
71+
72+
---
73+
74+
### 🚀 Quick Start
75+
76+
To run any of these examples, simply execute them with Python:
77+
78+
```bash
79+
python raw_hybrid_script.py
80+
```
81+
82+
--------
83+
84+
[<img src="https://seleniumbase.github.io/cdn/img/fancy_logo_14.png" title="SeleniumBase" width="290">](https://github.com/seleniumbase/SeleniumBase)

examples/migration/raw_selenium/to_playwright/01_hybrid_approach/non_test_scripts/__init__.py

Whitespace-only changes.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Hybrid Script Example"""
2+
from playwright.sync_api import sync_playwright, expect
3+
from selenium.webdriver.common.by import By
4+
from selenium.webdriver.support import expected_conditions as EC
5+
from selenium.webdriver.support.ui import WebDriverWait
6+
from seleniumbase import SB
7+
8+
9+
with SB(uc=True) as sb: # SeleniumBase API
10+
sb.activate_cdp_mode()
11+
sb.reconnect() # Reconnects WebDriver to use Selenium
12+
driver = sb.driver # Selenium API
13+
endpoint_url = sb.cdp.get_endpoint_url()
14+
with sync_playwright() as p:
15+
browser = p.chromium.connect_over_cdp(endpoint_url)
16+
page = browser.contexts[0].pages[0] # Playwright API
17+
18+
# This section uses Selenium
19+
driver.get("https://www.saucedemo.com")
20+
by_css = By.CSS_SELECTOR # "css selector"
21+
element = driver.find_element(by_css, "#user-name")
22+
element.send_keys("standard_user")
23+
element = driver.find_element(by_css, "#password")
24+
element.send_keys("secret_sauce")
25+
element.submit()
26+
driver.find_element(by_css, "div.inventory_list")
27+
element = driver.find_element(by_css, "span.title")
28+
assert element.text == "Products"
29+
30+
# This section uses Playwright
31+
page.click('button[name*="backpack"]')
32+
page.click("#shopping_cart_container a")
33+
expect(page.locator("span.title")).to_have_text("Your Cart")
34+
expect(page.locator("div.cart_item")).to_contain_text("Backpack")
35+
36+
# This section uses SeleniumBase
37+
sb.click("#remove-sauce-labs-backpack")
38+
sb.assert_element_not_visible("div.cart_item")
39+
40+
# This section uses Selenium
41+
driver.find_element(by_css, "#react-burger-menu-btn").click()
42+
element = WebDriverWait(driver, 10).until(
43+
EC.element_to_be_clickable((by_css, "a#logout_sidebar_link"))
44+
)
45+
element.click()
46+
driver.find_element(by_css, "input#login-button")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from playwright.sync_api import sync_playwright, expect
2+
3+
with sync_playwright() as p:
4+
browser = p.chromium.launch(channel="chrome", headless=False)
5+
page = browser.new_context().new_page()
6+
page.goto("https://www.saucedemo.com")
7+
page.fill("#user-name", "standard_user")
8+
page.fill("#password", "secret_sauce")
9+
page.press("#password", "Enter")
10+
expect(page.locator("div.inventory_list")).to_be_visible()
11+
expect(page.locator("span.title")).to_contain_text("Products")
12+
13+
page.click('button[name*="backpack"]')
14+
page.click("#shopping_cart_container a")
15+
expect(page.locator("span.title")).to_have_text("Your Cart")
16+
expect(page.locator("div.cart_item")).to_contain_text("Backpack")
17+
18+
page.click("#remove-sauce-labs-backpack")
19+
expect(page.locator("div.cart_item")).to_be_hidden()
20+
21+
page.click("#react-burger-menu-btn")
22+
page.click("a#logout_sidebar_link")
23+
expect(page.locator("input#login-button")).to_be_visible()
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from seleniumbase import SB
2+
3+
with SB() as sb:
4+
sb.open("https://www.saucedemo.com")
5+
sb.type("#user-name", "standard_user")
6+
sb.type("#password", "secret_sauce\n")
7+
sb.assert_element("div.inventory_list")
8+
sb.assert_text("Products", "span.title")
9+
10+
sb.click('button[name*="backpack"]')
11+
sb.click("#shopping_cart_container a")
12+
sb.assert_exact_text("Your Cart", "span.title")
13+
sb.assert_text("Backpack", "div.cart_item")
14+
15+
sb.click("#remove-sauce-labs-backpack")
16+
sb.assert_element_not_visible("div.cart_item")
17+
18+
sb.click("#react-burger-menu-btn")
19+
sb.click("a#logout_sidebar_link")
20+
sb.assert_element("input#login-button")
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import sys
2+
from selenium import webdriver
3+
from selenium.webdriver.chrome.service import Service
4+
from selenium.webdriver.common.by import By
5+
from selenium.webdriver.support import expected_conditions as EC
6+
from selenium.webdriver.support.ui import WebDriverWait
7+
8+
9+
def is_element_visible(driver, selector, by="css selector"):
10+
try:
11+
element = driver.find_element(by, selector)
12+
if element.is_displayed():
13+
return True
14+
except Exception:
15+
pass
16+
return False
17+
18+
19+
options = webdriver.ChromeOptions()
20+
options.add_argument("--disable-notifications")
21+
if "linux" in sys.platform:
22+
options.add_argument("--headless")
23+
options.add_experimental_option(
24+
"excludeSwitches", ["enable-automation", "enable-logging"],
25+
)
26+
prefs = {
27+
"credentials_enable_service": False,
28+
"profile.password_manager_enabled": False,
29+
"profile.password_manager_leak_detection": False,
30+
}
31+
options.add_experimental_option("prefs", prefs)
32+
service = Service(service_args=["--disable-build-check"])
33+
34+
35+
with webdriver.Chrome(options=options, service=service) as driver:
36+
driver.get("https://www.saucedemo.com")
37+
by_css = By.CSS_SELECTOR # "css selector"
38+
element = driver.find_element(by_css, "#user-name")
39+
element.send_keys("standard_user")
40+
element = driver.find_element(by_css, "#password")
41+
element.send_keys("secret_sauce")
42+
element.submit()
43+
driver.find_element(by_css, "div.inventory_list")
44+
element = driver.find_element(by_css, "span.title")
45+
assert element.text == "Products"
46+
47+
driver.find_element(by_css, 'button[name*="backpack"]').click()
48+
driver.find_element(by_css, "#shopping_cart_container a").click()
49+
element = driver.find_element(by_css, "span.title")
50+
assert element.text == "Your Cart"
51+
element = driver.find_element(by_css, "div.cart_item")
52+
assert "Backpack" in element.text
53+
54+
driver.find_element(by_css, "#remove-sauce-labs-backpack").click()
55+
assert not is_element_visible(driver, "div.cart_item")
56+
57+
driver.find_element(by_css, "#react-burger-menu-btn").click()
58+
element = WebDriverWait(driver, 10).until(
59+
EC.element_to_be_clickable((by_css, "a#logout_sidebar_link"))
60+
)
61+
element.click()
62+
driver.find_element(by_css, "input#login-button")
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Playwright TestCase Example"""
2+
import sys
3+
from playwright.sync_api import sync_playwright, expect
4+
from unittest import TestCase
5+
6+
7+
class PlaywrightTestCase(TestCase):
8+
@classmethod
9+
def setUpClass(cls):
10+
cls.playwright = sync_playwright().start()
11+
headless = "linux" in sys.platform or "--headless" in sys.argv
12+
# Launch browser once for the whole class
13+
cls.browser = cls.playwright.chromium.launch(
14+
channel="chrome", headless=headless
15+
)
16+
17+
@classmethod
18+
def tearDownClass(cls):
19+
cls.browser.close()
20+
cls.playwright.stop()
21+
22+
def setUp(self):
23+
# Create new session for every test
24+
self.context = self.browser.new_context()
25+
self.page = self.context.new_page()
26+
27+
def tearDown(self):
28+
# Close the session (browser runs until tearDownClass)
29+
self.page.close()
30+
self.context.close()
31+
32+
def test_add_item_to_cart(self):
33+
page = self.page
34+
35+
page.goto("https://www.saucedemo.com")
36+
page.fill("#user-name", "standard_user")
37+
page.fill("#password", "secret_sauce")
38+
page.press("#password", "Enter")
39+
expect(page.locator("div.inventory_list")).to_be_visible()
40+
expect(page.locator("span.title")).to_contain_text("Products")
41+
42+
page.click('button[name*="backpack"]')
43+
page.click("#shopping_cart_container a")
44+
expect(page.locator("span.title")).to_have_text("Your Cart")
45+
expect(page.locator("div.cart_item")).to_contain_text("Backpack")
46+
47+
page.click("#remove-sauce-labs-backpack")
48+
expect(page.locator("div.cart_item")).to_be_hidden()
49+
50+
page.click("#react-burger-menu-btn")
51+
page.click("a#logout_sidebar_link")
52+
expect(page.locator("input#login-button")).to_be_visible()
53+
54+
55+
if __name__ == "__main__":
56+
from pytest import main
57+
main([__file__, "-s"])
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""
2+
Playwright TestCase Example with Tracing.
3+
Trace logs are saved to: `latest_logs/[TESTPATH]/trace.zip`
4+
To open Trace Viewer: `playwright show-trace TRACEPATH.zip`
5+
"""
6+
import os
7+
import sys
8+
from playwright.sync_api import sync_playwright, expect
9+
from unittest import TestCase
10+
11+
12+
class PlaywrightTestCaseWithTracing(TestCase):
13+
@classmethod
14+
def setUpClass(cls):
15+
cls.playwright = sync_playwright().start()
16+
headless = "linux" in sys.platform or "--headless" in sys.argv
17+
# Launch browser once for the whole class
18+
cls.browser = cls.playwright.chromium.launch(
19+
channel="chrome", headless=headless
20+
)
21+
22+
@classmethod
23+
def tearDownClass(cls):
24+
cls.browser.close()
25+
cls.playwright.stop()
26+
27+
def setUp(self):
28+
# Create new session for every test
29+
self.context = self.browser.new_context()
30+
self.context.tracing.start(
31+
snapshots=True, screenshots=True, sources=True
32+
)
33+
self.page = self.context.new_page()
34+
35+
def tearDown(self):
36+
# Close the session (browser runs until tearDownClass)
37+
log_folder = "latest_logs"
38+
test_id = "%s.%s.%s" % (
39+
self.__class__.__module__,
40+
self.__class__.__name__,
41+
self._testMethodName,
42+
)
43+
trace_path = os.path.join(log_folder, test_id, "trace.zip")
44+
self.context.tracing.stop(path=trace_path)
45+
self.page.close()
46+
self.context.close()
47+
48+
def test_add_item_to_cart(self):
49+
page = self.page
50+
51+
page.goto("https://www.saucedemo.com")
52+
page.fill("#user-name", "standard_user")
53+
page.fill("#password", "secret_sauce")
54+
page.press("#password", "Enter")
55+
expect(page.locator("div.inventory_list")).to_be_visible()
56+
expect(page.locator("span.title")).to_contain_text("Products")
57+
58+
page.click('button[name*="backpack"]')
59+
page.click("#shopping_cart_container a")
60+
expect(page.locator("span.title")).to_have_text("Your Cart")
61+
expect(page.locator("div.cart_item")).to_contain_text("Backpack")
62+
63+
page.click("#remove-sauce-labs-backpack")
64+
expect(page.locator("div.cart_item")).to_be_hidden()
65+
66+
page.click("#react-burger-menu-btn")
67+
page.click("a#logout_sidebar_link")
68+
expect(page.locator("input#login-button")).to_be_visible()
69+
70+
71+
if __name__ == "__main__":
72+
from pytest import main
73+
main([__file__, "-s"])

0 commit comments

Comments
 (0)