Skip to content

Commit 006492e

Browse files
committed
Upload initial project files for Selenium-Python-E2E-Testing-Suite
1 parent c0bb812 commit 006492e

20 files changed

+536
-0
lines changed

.dockerignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Python
2+
__pycache__/
3+
*.pyc
4+
5+
# Environment
6+
venv/
7+
.venv/
8+
9+
# Test Results
10+
allure-results/
11+
test_report.html
12+
13+
# Caches
14+
.pytest_cache/
15+
16+
# IDE
17+
.idea/
18+
.vscode/

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Python
2+
__pycache__/
3+
*.pyc
4+
5+
# Environment
6+
venv/
7+
.venv/
8+
9+
# Test Results
10+
allure-results/
11+
test_report.html
12+
13+
# Caches
14+
.pytest_cache/
15+
16+
# IDE
17+
.idea/
18+
.vscode/

Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Use a slim Python 3.10 image as our base
2+
FROM python:3.10-slim
3+
4+
# Set the working directory inside the container
5+
WORKDIR /app
6+
7+
# Copy the requirements file and install dependencies
8+
COPY requirements.txt .
9+
RUN pip install --no-cache-dir -r requirements.txt
10+
11+
# Copy the rest of our project code into the container
12+
COPY . .

conftest.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import pytest
2+
import json
3+
from selenium import webdriver
4+
from selenium.webdriver.chrome.service import Service
5+
from webdriver_manager.chrome import ChromeDriverManager
6+
7+
8+
def pytest_addoption(parser):
9+
"""
10+
This is the correct hook to add custom INI options.
11+
"""
12+
parser.addini("base_url", help="The base URL for the application under test", default=None)
13+
14+
15+
@pytest.fixture(scope="session")
16+
def base_url(request):
17+
"""
18+
Fixture to get the base_url from the pytest.ini config file.
19+
"""
20+
return request.config.getini("base_url")
21+
22+
@pytest.fixture(scope="session")
23+
def test_data():
24+
"""
25+
Fixture to load test data from the JSON file.
26+
"""
27+
with open("data/test_data.json") as f:
28+
data = json.load(f)
29+
return data
30+
31+
32+
@pytest.fixture(scope="function")
33+
def driver():
34+
"""
35+
This fixture creates a new REMOTE Chrome WebDriver instance.
36+
It connects to the 'selenium' service defined in docker-compose.yml.
37+
"""
38+
39+
# --- Setup Chrome Options ---
40+
chrome_options = webdriver.ChromeOptions()
41+
chrome_options.add_argument("--incognito")
42+
chrome_options.add_argument("--start-maximized")
43+
chrome_options.add_argument("--disable-extensions")
44+
chrome_options.add_argument("--disable-features=PasswordLeakDetection")
45+
46+
prefs = {
47+
"credentials_enable_service": False,
48+
"profile.password_manager_enabled": False,
49+
"profile.password_manager_leak_detection_enabled": False
50+
}
51+
chrome_options.add_experimental_option("prefs", prefs)
52+
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
53+
54+
# --- Driver Initialization ---
55+
# The URL now points to our new service name: 'selenium'
56+
REMOTE_HUB_URL = "http://selenium:4444/wd/hub"
57+
58+
# No more try/except. When running in Docker, it MUST
59+
# connect to this URL, or we want it to fail.
60+
driver_instance = webdriver.Remote(
61+
command_executor=REMOTE_HUB_URL,
62+
options=chrome_options
63+
)
64+
65+
yield driver_instance
66+
67+
# --- Teardown ---
68+
driver_instance.quit()

data/test_data.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"users": {
3+
"standard": {
4+
"username": "standard_user",
5+
"password": "secret_sauce"
6+
},
7+
"locked_out": {
8+
"username": "locked_out_user",
9+
"password": "secret_sauce"
10+
},
11+
"invalid": {
12+
"username": "invalid_user",
13+
"password": "invalid_password"
14+
}
15+
},
16+
"error_messages": {
17+
"locked_out": "Epic sadface: Sorry, this user has been locked out.",
18+
"invalid": "Epic sadface: Username and password do not match any user in this service"
19+
}
20+
}

docker-compose.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
services:
2+
# This is the standalone Selenium container (Hub + Chrome in one)
3+
selenium:
4+
image: selenium/standalone-chrome:4.20.0
5+
container_name: selenium
6+
ports:
7+
- "4444:4444" # Expose the hub port to our host machine
8+
shm_size: '2g' # Add shared memory to prevent Chrome crashes
9+
10+
# --- THIS IS THE MAGIC ---
11+
# Docker will run this command inside the 'selenium' container
12+
# until it returns success (exit code 0).
13+
healthcheck:
14+
test: ["CMD", "curl", "-f", "http://localhost:4444/wd/hub/status"]
15+
interval: 5s # Check every 5 seconds
16+
timeout: 10s # Wait 10s for the check to complete
17+
retries: 10 # Try 10 times before failing
18+
start_period: 5s # Give it 5s to boot before first check
19+
20+
# This is our Python test container
21+
tests:
22+
container_name: python-tests
23+
build: . # Build the image from the Dockerfile in this directory
24+
volumes:
25+
- ./allure-results:/app/allure-results # Map results folder
26+
27+
# --- THIS IS THE OTHER HALF OF THE MAGIC ---
28+
# This tells 'tests' to NOT start until the 'selenium'
29+
# service's 'healthcheck' has passed. No more race conditions!
30+
depends_on:
31+
selenium:
32+
condition: service_healthy
33+
34+
# Now, our command is simple. It just runs pytest.
35+
# It will only run AFTER the healthcheck is green.
36+
command: pytest

pages/__init__.py

Whitespace-only changes.

pages/base_page.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from selenium.webdriver.support.ui import WebDriverWait
2+
from selenium.webdriver.support import expected_conditions as EC
3+
4+
class BasePage:
5+
"""
6+
The Base Page class, which all other Page Objects will inherit from.
7+
"""
8+
def __init__(self, driver, base_url):
9+
"""
10+
Initialize the Base Page with the WebDriver instance.
11+
"""
12+
self.driver = driver
13+
self.base_url = base_url
14+
self.wait = WebDriverWait(driver, 10) # 10-second explicit wait
15+
16+
def find(self, locator):
17+
"""
18+
Finds a single element using the specified locator.
19+
Waits until the element is present.
20+
"""
21+
return self.wait.until(EC.presence_of_element_located(locator))
22+
23+
def find_all(self, locator):
24+
"""
25+
Finds all elements using the specified locator.
26+
Waits until at least one element is present.
27+
"""
28+
return self.wait.until(EC.presence_of_all_elements_located(locator))
29+
30+
def click(self, locator):
31+
"""
32+
Waits for an element to be clickable, then clicks it.
33+
"""
34+
element = self.wait.until(EC.element_to_be_clickable(locator))
35+
element.click()
36+
37+
def type_text(self, locator, text):
38+
"""
39+
Finds an element, clears it, and types the given text.
40+
"""
41+
element = self.find(locator)
42+
element.clear()
43+
element.send_keys(text)
44+
45+
def get_text(self, locator):
46+
"""
47+
Finds an element and returns its text content.
48+
"""
49+
element = self.find(locator)
50+
return element.text

pages/cart_page.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from selenium.webdriver.common.by import By
2+
from .base_page import BasePage
3+
4+
class CartPage(BasePage):
5+
"""
6+
Page Object for the Shopping Cart page.
7+
"""
8+
PATH = "/cart.html"
9+
10+
# Locators
11+
PAGE_TITLE = (By.CLASS_NAME, "title")
12+
ITEM_NAME = (By.CLASS_NAME, "inventory_item_name")
13+
CHECKOUT_BUTTON = (By.ID, "checkout")
14+
15+
def get_page_title(self):
16+
"""Returns the text of the page title."""
17+
return self.get_text(self.PAGE_TITLE)
18+
19+
def get_item_name(self):
20+
"""Returns the text of the first item in the cart."""
21+
# Note: This is a simple implementation that gets the first item.
22+
# For a real framework, you'd want to get all items.
23+
return self.get_text(self.ITEM_NAME)
24+
25+
def go_to_checkout(self):
26+
"""Clicks the 'Checkout' button."""
27+
self.click(self.CHECKOUT_BUTTON)

pages/checkout_complete_page.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from selenium.webdriver.common.by import By
2+
from .base_page import BasePage
3+
4+
class CheckoutCompletePage(BasePage):
5+
"""
6+
Page Object for the final 'Thank You' page.
7+
"""
8+
PATH = "/checkout-complete.html"
9+
10+
# Locators
11+
PAGE_TITLE = (By.CLASS_NAME, "title")
12+
COMPLETE_HEADER = (By.CLASS_NAME, "complete-header")
13+
14+
def get_page_title(self):
15+
"""Returns the text of the page title."""
16+
return self.get_text(self.PAGE_TITLE)
17+
18+
def get_complete_header_text(self):
19+
"""Returns the 'Thank You' message text."""
20+
return self.get_text(self.COMPLETE_HEADER)

0 commit comments

Comments
 (0)