Skip to content

Commit c27c578

Browse files
committed
Complete cart tests with verify, remove single/all, and checkout start
1 parent 932e0d0 commit c27c578

7 files changed

Lines changed: 254 additions & 20 deletions

File tree

config/config_for_cart_page.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from selenium.webdriver.common.by import By
2+
3+
PRODUCTS = {
4+
"bike_light": {
5+
"add": (By.ID, "add-to-cart-sauce-labs-bike-light"),
6+
"remove": (By.ID, "remove-sauce-labs-bike-light"),
7+
"display_name": "Sauce Labs Bike Light"
8+
},
9+
"backpack": {
10+
"add": (By.ID, "add-to-cart-sauce-labs-backpack"),
11+
"remove": (By.ID, "remove-sauce-labs-backpack"),
12+
"display_name": "Sauce Labs Backpack"
13+
},
14+
"fleece_jacket": {
15+
"add": (By.ID, "add-to-cart-sauce-labs-fleece-jacket"),
16+
"remove": (By.ID, "remove-sauce-labs-fleece-jacket"),
17+
"display_name": "Sauce Labs Fleece Jacket"
18+
},
19+
"onesie": {
20+
"add": (By.ID, "add-to-cart-sauce-labs-onesie"),
21+
"remove": (By.ID, "remove-sauce-labs-onesie"),
22+
"display_name": "Sauce Labs Onesie"
23+
},
24+
"bolt_tshirt": {
25+
"add": (By.ID, "add-to-cart-sauce-labs-bolt-t-shirt"),
26+
"remove": (By.ID, "remove-sauce-labs-bolt-t-shirt"),
27+
"display_name": "Sauce Labs Bolt T-Shirt"
28+
},
29+
"red_tshirt": {
30+
"add": (By.ID, "add-to-cart-test.allthethings()-t-shirt-(red)"),
31+
"remove": (By.ID, "remove-test.allthethings()-t-shirt-(red)"),
32+
"display_name": "Test.allTheThings() T-Shirt (Red)"
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from selenium.webdriver.common.by import By
2+
3+
PRODUCTS = {
4+
"bike_light": {
5+
"add": (By.ID, "add-to-cart-sauce-labs-bike-light"),
6+
"remove": (By.ID, "remove-sauce-labs-bike-light"),
7+
"display_name": "Sauce Labs Bike Light"
8+
},
9+
"backpack": {
10+
"add": (By.ID, "add-to-cart-sauce-labs-backpack"),
11+
"remove": (By.ID, "remove-sauce-labs-backpack"),
12+
"display_name": "Sauce Labs Backpack"
13+
},
14+
"fleece_jacket": {
15+
"add": (By.ID, "add-to-cart-sauce-labs-fleece-jacket"),
16+
"remove": (By.ID, "remove-sauce-labs-fleece-jacket"),
17+
"display_name": "Sauce Labs Fleece Jacket"
18+
},
19+
"onesie": {
20+
"add": (By.ID, "add-to-cart-sauce-labs-onesie"),
21+
"remove": (By.ID, "remove-sauce-labs-onesie"),
22+
"display_name": "Sauce Labs Onesie"
23+
},
24+
"bolt_tshirt": {
25+
"add": (By.ID, "add-to-cart-sauce-labs-bolt-t-shirt"),
26+
"remove": (By.ID, "remove-sauce-labs-bolt-t-shirt"),
27+
"display_name": "Sauce Labs Bolt T-Shirt"
28+
},
29+
"red_tshirt": {
30+
"add": (By.ID, "add-to-cart-test.allthethings()-t-shirt-(red)"),
31+
"remove": (By.ID, "remove-test.allthethings()-t-shirt-(red)"),
32+
"display_name": "Test.allTheThings() T-Shirt (Red)"
33+
}
34+
}

config/config_for_login_page.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
12
CREDENTIALS = {
23
"standard_user": {"username": "standard_user", "password": "secret_sauce"},
34
"locked_out_user": {"username": "locked_out_user", "password": "secret_sauce"},
45
"invalid_user": {"username": "invalid_user", "password": "wrong_password"},
56
"empty_user": {"username": "", "password": "secret_sauce"}
6-
}
7+
}
8+

pages/cart_page.py

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,43 @@
11
from selenium.webdriver import Keys
22
from selenium.webdriver.common.by import By
33
from selenium.webdriver.support import expected_conditions as EC
4+
from selenium.webdriver.support.expected_conditions import staleness_of
45
from selenium.webdriver.support.ui import WebDriverWait
5-
from selenium.common.exceptions import TimeoutException
6-
6+
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException
7+
from config.config_for_cart_page import PRODUCTS
8+
from selenium.common.exceptions import NoSuchElementException
79
class CartPage:
810
CART_ITEMS = (By.CLASS_NAME, "cart_item")
911
ITEM_NAME = (By.CLASS_NAME, "inventory_item_name")
1012
CHECKOUT_BUTTON = (By.ID, 'checkout')
13+
CART_BADGE = (By.CLASS_NAME, "shopping_cart_badge")
1114

1215
def __init__(self, driver):
1316
self.driver = driver
1417
self.wait = WebDriverWait(self.driver, 10)
1518

1619
def get_cart_items(self):
17-
self.wait.until(EC.presence_of_all_elements_located(CartPage.CART_ITEMS))
18-
items = self.driver.find_elements(*CartPage.CART_ITEMS)
19-
item_name = [item.find_element(*self.ITEM_NAME).text for item in items]
20-
return item_name
20+
try:
21+
self.wait.until(EC.presence_of_all_elements_located(CartPage.CART_ITEMS))
22+
except TimeoutException:
23+
return []
24+
present_items = []
25+
for product_key in PRODUCTS:
26+
try:
27+
remove_selector = PRODUCTS[product_key]['remove']
28+
self.driver.find_element(*remove_selector)
29+
present_items.append(PRODUCTS[product_key]['display_name'])
30+
except NoSuchElementException:
31+
pass
32+
return present_items
33+
34+
35+
def get_cart_badge_count(self):
36+
try:
37+
self.wait.until(EC.presence_of_element_located(CartPage.CART_BADGE))
38+
return self.driver.find_element(*CartPage.CART_BADGE).text
39+
except TimeoutException:
40+
return "0"
2141

2242

2343
def is_checkout_button_visible(self):
@@ -27,3 +47,29 @@ def is_checkout_button_visible(self):
2747
except TimeoutException:
2848
return False
2949

50+
51+
def remove_item(self,product_key):
52+
if PRODUCTS[product_key]['display_name'] not in self.get_cart_items():
53+
print(f'Product {product_key} not found.')
54+
return False
55+
56+
remove_selector= PRODUCTS[product_key]['remove']
57+
try:
58+
remove_button = self.wait.until(EC.element_to_be_clickable(remove_selector))
59+
remove_button.click()
60+
self.wait.until(EC.staleness_of(remove_button))
61+
print(f"Product '{product_key}' removed, new badge: {self.get_cart_badge_count()}")
62+
return True
63+
except (StaleElementReferenceException,TimeoutException):
64+
return False
65+
66+
def start_checkout(self):
67+
if not self.is_checkout_button_visible():
68+
return False
69+
checkout_button = self.wait.until(EC.element_to_be_clickable(CartPage.CHECKOUT_BUTTON))
70+
checkout_button.click()
71+
try:
72+
self.wait.until(EC.url_contains("checkout-step-one"))
73+
return True
74+
except TimeoutException:
75+
return False

pages/inventory_page.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,57 @@
33
from selenium.webdriver.support import expected_conditions as EC
44
from selenium.webdriver.support.ui import WebDriverWait
55
from selenium.common.exceptions import TimeoutException
6-
6+
from config.config_for_inventory_page import PRODUCTS
77

88
class InventoryPage:
9-
ADD_CART_BUTTON= (By.ID, 'add-to-cart-sauce-labs-bike-light')
10-
REMOVE_BUTTON = (By.ID, 'remove-sauce-labs-bike-light')
119
CART_BUTTON = (By.CLASS_NAME, "shopping_cart_link")
1210
CART_BADGE = (By.CLASS_NAME, "shopping_cart_badge")
11+
SORT_DROPDOWN = (By.CLASS_NAME, "product_sort_container")
12+
PRICE_SELECTOR = (By.CLASS_NAME, "inventory_item_price")
1313

1414
def __init__(self, driver):
1515
self.driver = driver
1616
self.wait = WebDriverWait(self.driver, 10)
1717

18-
19-
20-
def add_product_to_cart(self):
21-
self.wait.until(EC.visibility_of_element_located(InventoryPage.ADD_CART_BUTTON))
22-
self.driver.find_element(*InventoryPage.ADD_CART_BUTTON).click()
23-
self.wait.until(EC.visibility_of_element_located(InventoryPage.REMOVE_BUTTON))
18+
def add_product(self, product_key):
19+
add_selector = PRODUCTS[product_key]["add"]
20+
self.wait.until(EC.visibility_of_element_located(add_selector))
21+
self.driver.find_element(*add_selector).click()
22+
remove_selector = PRODUCTS[product_key]["remove"]
23+
self.wait.until(EC.visibility_of_element_located(remove_selector))
24+
25+
def add_multiple_products(self, products_list):
26+
if len(products_list) == 0:
27+
return
28+
expected_count = len(products_list)
29+
for product in products_list:
30+
self.add_product(product)
31+
self.wait.until(EC.text_to_be_present_in_element(InventoryPage.CART_BADGE, str(expected_count)))
2432

2533
def get_cart_badge_count(self):
2634
try:
27-
self.wait.until(EC.presence_of_element_located(self.CART_BADGE))
28-
return self.driver.find_element(*self.CART_BADGE).text
35+
self.wait.until(EC.presence_of_element_located(InventoryPage.CART_BADGE))
36+
return self.driver.find_element(*InventoryPage.CART_BADGE).text
2937
except TimeoutException:
3038
return "0"
3139

32-
3340
def go_to_cart(self):
3441
self.wait.until(EC.visibility_of_element_located(InventoryPage.CART_BUTTON))
3542
self.driver.find_element(*InventoryPage.CART_BUTTON).click()
3643

44+
def sort_by_price(self, option_value):
45+
self.wait.until(EC.visibility_of_element_located(InventoryPage.SORT_DROPDOWN))
46+
self.driver.find_element(*InventoryPage.SORT_DROPDOWN).click()
47+
option= self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, f'select option[value="{option_value}"]')))
48+
option.click()
49+
self.wait.until(EC.presence_of_element_located(InventoryPage.PRICE_SELECTOR))
50+
51+
def get_prices(self):
52+
prices_elements= self.driver.find_elements(*InventoryPage.PRICE_SELECTOR)
53+
prices = []
54+
for elem in prices_elements:
55+
price_text = elem.text
56+
price = float(price_text.replace("$", ""))
57+
prices.append(price)
58+
return prices
3759

test/test_cart.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import pytest
2+
from selenium import webdriver
3+
from selenium.webdriver.firefox.service import Service
4+
from selenium.webdriver.firefox.options import Options as FirefoxOptions
5+
from pages.inventory_page import InventoryPage
6+
from pages.login_page import LoginPage
7+
from pages.cart_page import CartPage
8+
from webdriver_manager.firefox import GeckoDriverManager
9+
from config import config_for_login_page
10+
from config.config_for_cart_page import PRODUCTS
11+
12+
@pytest.fixture(scope="function")
13+
def cart_page_with_items(request):
14+
firefox_options = FirefoxOptions()
15+
firefox_options.add_argument("--disable-notifications")
16+
firefox_options.add_argument("--headless")
17+
firefox_options.set_preference("security.password_lifetime", 0)
18+
firefox_options.set_preference("signon.rememberSignons", False)
19+
firefox_options.set_preference("signon.autofillForms", False)
20+
firefox_options.set_preference("privacy.trackingprotection.enabled", True)
21+
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()), options=firefox_options)
22+
login_page = LoginPage(driver)
23+
login_page.open_page()
24+
login_page.insert_user_name(config_for_login_page.CREDENTIALS["standard_user"]["username"])
25+
login_page.insert_password(config_for_login_page.CREDENTIALS["standard_user"]["password"])
26+
login_page.click_login_button()
27+
assert login_page.driver.current_url == "https://www.saucedemo.com/inventory.html"
28+
inventory = InventoryPage(driver)
29+
inventory.add_multiple_products(["bike_light", "backpack"])
30+
inventory.go_to_cart()
31+
cart_page = CartPage(driver)
32+
request.addfinalizer(driver.quit)
33+
return cart_page
34+
35+
36+
def test_verify_cart_items(cart_page_with_items):
37+
items = cart_page_with_items.get_cart_items()
38+
badge = cart_page_with_items.get_cart_badge_count()
39+
assert len(items)==2
40+
assert "Sauce Labs Bike Light" in items
41+
assert badge == '2'
42+
print(f"Items: {items}, Badge: {badge}")
43+
44+
@pytest.mark.parametrize("product_key, expected_badge_after", [("bike_light",'1'), ("backpack",'1')])
45+
46+
def test_remove_single_item(cart_page_with_items, product_key, expected_badge_after):
47+
success = cart_page_with_items.remove_item(product_key)
48+
items_after = cart_page_with_items.get_cart_items()
49+
badge_after = cart_page_with_items.get_cart_badge_count()
50+
assert success == True
51+
assert badge_after == expected_badge_after
52+
assert PRODUCTS [product_key]["display_name"] not in items_after
53+
54+
def test_remove_all_items(cart_page_with_items):
55+
products = ["bike_light", "backpack"]
56+
for p in products:
57+
cart_page_with_items.remove_item(p)
58+
59+
assert len(cart_page_with_items.get_cart_items()) == 0
60+
assert cart_page_with_items.get_cart_badge_count() == '0'
61+
62+
def test_start_checkout(cart_page_with_items):
63+
assert cart_page_with_items.is_checkout_button_visible() == True
64+
cart_page_with_items.start_checkout()
65+
assert "checkout-step-one.html" in cart_page_with_items.driver.current_url
66+
print(f"Current URL after checkout: {cart_page_with_items.driver.current_url}")
67+

test/test_inventory.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def inventory_page(request):
2929
return inventory_page
3030

3131
def test_add_product_to_cart(inventory_page):
32-
inventory_page.add_product_to_cart()
32+
inventory_page.add_product("bike_light")
3333
badge_count = inventory_page.get_cart_badge_count()
3434
assert inventory_page.get_cart_badge_count() == "1"
3535
print(f"Badge count: {badge_count}")
@@ -41,6 +41,35 @@ def test_add_product_to_cart(inventory_page):
4141
assert cart_page.is_checkout_button_visible(), "Checkout button is not visible"
4242

4343

44+
def test_multi_add(inventory_page):
45+
products = ["bike_light", "backpack", "fleece_jacket"]
46+
inventory_page.add_multiple_products(products)
47+
badge = int(inventory_page.get_cart_badge_count())
48+
assert badge == 3
49+
50+
@pytest.mark.parametrize("sort_option, expected_order", [("lohi", "asc"), ("hilo", "desc")])
51+
52+
def test_sort_order(inventory_page, sort_option, expected_order):
53+
prices_before = inventory_page.get_prices()
54+
inventory_page.sort_by_price(sort_option)
55+
prices_after = inventory_page.get_prices()
56+
assert len(prices_after) == 6
57+
expected_sorted = sorted(prices_before, reverse=(expected_order == "desc"))
58+
assert prices_after == expected_sorted
59+
60+
def test_get_prices(inventory_page):
61+
prices = inventory_page.get_prices()
62+
assert len(prices) == 6
63+
assert all(isinstance(p, float) for p in prices)
64+
assert min(prices) >0
65+
66+
67+
68+
69+
70+
71+
72+
4473

4574

4675

0 commit comments

Comments
 (0)