Skip to content

Commit efd25e6

Browse files
Merge pull request #2 from BrowserStackCE/tc/fix-playwright-firefox-onboarding
Add Selenium pytest onboarding setup with BrowserStack SDK
2 parents 01524ed + 8926457 commit efd25e6

5 files changed

Lines changed: 216 additions & 0 deletions

File tree

‎Selenium/README.md‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Selenium pytest — BrowserStack Automate
2+
3+
Equivalent of the Playwright `e2e-login-add-transaction` spec, ported to **Selenium 4 + pytest** and wired to **BrowserStack Automate** via the BrowserStack SDK.
4+
5+
## Prerequisites
6+
7+
- Python 3.9+
8+
- Google Chrome (for local runs)
9+
10+
## Setup
11+
12+
**macOS / Linux:**
13+
```bash
14+
cd Selenium
15+
python3 -m venv .venv
16+
source .venv/bin/activate
17+
pip install -r requirements.txt
18+
```
19+
20+
**Windows (PowerShell):**
21+
```powershell
22+
cd Selenium
23+
python -m venv .venv
24+
.venv\Scripts\Activate.ps1
25+
pip install -r requirements.txt
26+
```
27+
28+
## Run locally
29+
30+
Uses `browserstack-sdk pytest` with `BROWSERSTACK_AUTOMATION=false` — the SDK still reports results to Test Observability but runs Chrome locally without spinning up cloud browsers.
31+
32+
**macOS / Linux:**
33+
```bash
34+
source .venv/bin/activate
35+
export BROWSERSTACK_USERNAME=<your_username>
36+
export BROWSERSTACK_ACCESS_KEY=<your_access_key>
37+
export BROWSERSTACK_AUTOMATION=false
38+
browserstack-sdk pytest tests/ -v
39+
```
40+
41+
**Windows (PowerShell):**
42+
```powershell
43+
.venv\Scripts\Activate.ps1
44+
$env:BROWSERSTACK_USERNAME="<your_username>"
45+
$env:BROWSERSTACK_ACCESS_KEY="<your_access_key>"
46+
$env:BROWSERSTACK_AUTOMATION="false"
47+
browserstack-sdk pytest tests/ -v
48+
```
49+
50+
## Run on BrowserStack
51+
52+
Set `BROWSERSTACK_AUTOMATION=true` so the BrowserStack SDK intercepts the `webdriver.Chrome()` call and redirects it to the BrowserStack hub.
53+
54+
**macOS / Linux:**
55+
```bash
56+
source .venv/bin/activate
57+
export BROWSERSTACK_USERNAME=<your_username>
58+
export BROWSERSTACK_ACCESS_KEY=<your_access_key>
59+
export BROWSERSTACK_AUTOMATION=true
60+
browserstack-sdk pytest tests/ -v
61+
```
62+
63+
**Windows (PowerShell):**
64+
```powershell
65+
.venv\Scripts\Activate.ps1
66+
$env:BROWSERSTACK_USERNAME="<your_username>"
67+
$env:BROWSERSTACK_ACCESS_KEY="<your_access_key>"
68+
$env:BROWSERSTACK_AUTOMATION="true"
69+
browserstack-sdk pytest tests/ -v
70+
```
71+
72+
Credentials are read from environment variables. `BROWSERSTACK_AUTOMATION=true` routes the WebDriver to the BrowserStack hub; `BROWSERSTACK_AUTOMATION=false` keeps it local while still reporting to Test Observability.

‎Selenium/browserstack.yml‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
userName: ${BROWSERSTACK_USERNAME}
2+
accessKey: ${BROWSERSTACK_ACCESS_KEY}
3+
4+
platforms:
5+
# Desktop Platforms (5 Parallels)
6+
- browserName: chrome
7+
browserVersion: latest
8+
os: Windows
9+
osVersion: 11
10+
- browserName: safari
11+
browserVersion: latest
12+
os: OSX
13+
osVersion: Sonoma
14+
- browserName: edge
15+
browserVersion: latest
16+
os: Windows
17+
osVersion: 10
18+
- browserName: firefox
19+
browserVersion: latest
20+
os: Windows
21+
osVersion: 11
22+
- browserName: chrome
23+
browserVersion: latest
24+
os: OSX
25+
osVersion: Ventura
26+
27+
parallelsPerPlatform: 1
28+
29+
browserstackLocal: true
30+
31+
buildName: "Onboarding_Build"
32+
projectName: "Automate_Onboarding"
33+
buildTag: "e2e"
34+
35+
framework: pytest
36+
37+
testObservability: true
38+
debug: true
39+
networkLogs: true
40+
consoleLogs: info
41+
interactiveDebugging: true

‎Selenium/conftest.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
import pytest
3+
from selenium import webdriver
4+
5+
6+
@pytest.fixture(scope="function")
7+
def driver():
8+
"""WebDriver fixture.
9+
10+
Always creates a local webdriver.Chrome — the BrowserStack SDK intercepts
11+
this call and redirects it to the BrowserStack hub when the test is run via
12+
`browserstack-sdk pytest …` (i.e. when BROWSERSTACK_AUTOMATION=true).
13+
For plain local runs, Chrome is used directly without any interception.
14+
"""
15+
options = webdriver.ChromeOptions()
16+
driver = webdriver.Chrome(options=options)
17+
driver.implicitly_wait(10)
18+
yield driver
19+
driver.quit()

‎Selenium/requirements.txt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
selenium==4.27.1
2+
pytest==8.3.5
3+
browserstack-sdk==1.51.2
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import pytest
2+
from selenium.webdriver.common.by import By
3+
from selenium.webdriver.support.ui import WebDriverWait
4+
from selenium.webdriver.support import expected_conditions as EC
5+
6+
BASE_URL = "https://finstack-alpha.vercel.app"
7+
8+
9+
def test_e2e_login_and_add_expense_transaction(driver):
10+
"""T001: E2E: Login and Add an Expense Transaction"""
11+
12+
wait = WebDriverWait(driver, 30)
13+
14+
# Step 1: Navigate to login page
15+
driver.get(f"{BASE_URL}/login")
16+
wait.until(EC.visibility_of_element_located((By.ID, "sign_in")))
17+
18+
# Step 2: Enter email
19+
driver.find_element(By.ID, "email").send_keys("testuser@bstackbank.com")
20+
21+
# Step 3: Enter password
22+
driver.find_element(By.ID, "password").send_keys("Test@1234")
23+
24+
# Step 4: Click Sign In and wait for dashboard
25+
driver.find_element(By.ID, "sign_in").click()
26+
wait.until(EC.url_contains("/dashboard"))
27+
wait.until(EC.visibility_of_element_located(
28+
(By.XPATH, "//h1[contains(text(), 'Good morning')]")
29+
))
30+
31+
# Step 5: Click '+ Add Transaction' button
32+
add_btn = wait.until(EC.element_to_be_clickable(
33+
(By.XPATH, "//button[normalize-space()='Add Transaction']")
34+
))
35+
add_btn.click()
36+
wait.until(EC.visibility_of_element_located(
37+
(By.CSS_SELECTOR, "[data-slot='dialog-content']")
38+
))
39+
40+
# Step 6: Enter amount
41+
amount_field = wait.until(EC.visibility_of_element_located((By.ID, "amount")))
42+
amount_field.clear()
43+
amount_field.send_keys("150.00")
44+
45+
# Step 7: Enter description
46+
driver.find_element(By.ID, "description").send_keys("Grocery Shopping")
47+
48+
# Step 8 & 9: Select Category → Food & Dining
49+
category_btn = driver.find_element(
50+
By.XPATH,
51+
"//div[contains(@class,'grid') and contains(@class,'gap-2') and .//text()[contains(.,'Category')]]//button[contains(@class,'border-input')]"
52+
)
53+
category_btn.click()
54+
wait.until(EC.element_to_be_clickable(
55+
(By.XPATH, "//div[@role='option' and normalize-space()='Food & Dining']")
56+
)).click()
57+
58+
# Step 10 & 11: Select Account → Main Checking
59+
account_btn = driver.find_element(
60+
By.XPATH,
61+
"//div[contains(@class,'grid') and contains(@class,'gap-2') and .//text()[contains(.,'Account')]]//button[contains(@class,'border-input')]"
62+
)
63+
account_btn.click()
64+
wait.until(EC.element_to_be_clickable(
65+
(By.XPATH, "//div[@role='option' and normalize-space()='Main Checking']")
66+
)).click()
67+
68+
# Step 12: Submit the transaction — scoped inside the dialog to avoid matching the header button
69+
submit_btn = wait.until(EC.element_to_be_clickable(
70+
(By.XPATH, "//*[@data-slot='dialog-content']//button[normalize-space()='Add Transaction']")
71+
))
72+
driver.execute_script("arguments[0].scrollIntoView(true);", submit_btn)
73+
driver.execute_script("arguments[0].click();", submit_btn)
74+
75+
# Step 13: Verify modal closes and dashboard is shown
76+
wait.until(EC.invisibility_of_element_located(
77+
(By.CSS_SELECTOR, "[data-slot='dialog-content']")
78+
))
79+
wait.until(EC.visibility_of_element_located(
80+
(By.XPATH, "//h1[contains(text(), 'Good morning')]")
81+
))

0 commit comments

Comments
 (0)